libostree: Nearly complete move of API into OstreeSysroot
authorColin Walters <walters@verbum.org>
Mon, 16 Sep 2013 00:16:20 +0000 (20:16 -0400)
committerColin Walters <walters@verbum.org>
Mon, 16 Sep 2013 00:16:20 +0000 (20:16 -0400)
Move the deployment code too.

20 files changed:
Makefile-libostree.am
Makefile-ostree.am
src/libostree/ostree-ordered-hash.c [new file with mode: 0644]
src/libostree/ostree-ordered-hash.h [new file with mode: 0644]
src/libostree/ostree-sysroot-deploy.c [new file with mode: 0644]
src/libostree/ostree-sysroot-private.h
src/libostree/ostree-sysroot.c
src/libostree/ostree-sysroot.h
src/ostree/ot-admin-builtin-deploy.c
src/ostree/ot-admin-builtin-diff.c
src/ostree/ot-admin-builtin-status.c
src/ostree/ot-admin-builtin-undeploy.c
src/ostree/ot-admin-builtin-upgrade.c
src/ostree/ot-admin-deploy.c [deleted file]
src/ostree/ot-admin-deploy.h [deleted file]
src/ostree/ot-admin-functions.c
src/ostree/ot-admin-functions.h
src/ostree/ot-admin-util.c [deleted file]
src/ostree/ot-ordered-hash.c [deleted file]
src/ostree/ot-ordered-hash.h [deleted file]

index 3576b4404ef9ae3e2ff5197a0ef0355c39d9c8b3..d9ee1f1051f79894f17dce0c1b2ae6de41d33d74 100644 (file)
@@ -46,6 +46,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-sysroot-private.h \
        src/libostree/ostree-sysroot.c \
        src/libostree/ostree-sysroot-cleanup.c \
+       src/libostree/ostree-sysroot-deploy.c \
        src/libostree/ostree-bootconfig-parser.c \
        src/libostree/ostree-deployment.c \
        src/libostree/ostree-bootloader.c \
@@ -53,6 +54,8 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-bootloader-syslinux.c \
        src/libostree/ostree-bootloader-uboot.h \
        src/libostree/ostree-bootloader-uboot.c \
+       src/libostree/ostree-ordered-hash.h \
+       src/libostree/ostree-ordered-hash.c \
        $(NULL)
 if USE_LIBARCHIVE
 libostree_1_la_SOURCES += src/libostree/ostree-libarchive-input-stream.h \
index df5c3a41d64332caf82233a7709063c1309bebc5..f2a3615e6e7a5e1139ea67db112d7bc6bc6246c6 100644 (file)
@@ -59,13 +59,8 @@ ostree_SOURCES += \
        src/ostree/ot-admin-builtin-status.c \
        src/ostree/ot-admin-builtin-upgrade.c \
        src/ostree/ot-admin-builtins.h \
-       src/ostree/ot-admin-util.c \
        src/ostree/ot-admin-functions.h \
        src/ostree/ot-admin-functions.c \
-       src/ostree/ot-admin-deploy.h \
-       src/ostree/ot-admin-deploy.c \
-       src/ostree/ot-ordered-hash.h \
-       src/ostree/ot-ordered-hash.c \
        $(NULL)
 
 ostree_bin_shared_cflags = $(AM_CFLAGS) -I$(srcdir)/src/libgsystem -I$(srcdir)/src/libotutil -I$(srcdir)/src/libostree -I$(srcdir)/src/ostree  -DLOCALEDIR=\"$(datadir)/locale\"
diff --git a/src/libostree/ostree-ordered-hash.c b/src/libostree/ostree-ordered-hash.c
new file mode 100644 (file)
index 0000000..ba13bf9
--- /dev/null
@@ -0,0 +1,83 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "ostree-ordered-hash.h"
+#include "libgsystem.h"
+
+OstreeOrderedHash *
+_ostree_ordered_hash_new (void)
+{
+  OstreeOrderedHash *ret;
+  ret = g_new0 (OstreeOrderedHash, 1);
+  ret->order = g_ptr_array_new_with_free_func (g_free);
+  ret->table = g_hash_table_new (g_str_hash, g_str_equal);
+  return ret;
+}
+
+void
+_ostree_ordered_hash_free (OstreeOrderedHash *ohash)
+{
+  if (!ohash)
+    return;
+  g_ptr_array_unref (ohash->order);
+  g_hash_table_unref (ohash->table);
+  g_free (ohash);
+}
+
+void
+_ostree_ordered_hash_cleanup (void *loc)
+{
+  _ostree_ordered_hash_free (*((OstreeOrderedHash**)loc));
+}
+
+void
+_ostree_ordered_hash_replace_key_take (OstreeOrderedHash   *ohash,
+                                  char            *key,
+                                  const char      *value)
+{
+  gboolean existed;
+
+  existed = g_hash_table_remove (ohash->table, key);
+  if (!existed)
+    g_ptr_array_add (ohash->order, key);
+  g_hash_table_insert (ohash->table, key, (char*)value);
+}
+
+void
+_ostree_ordered_hash_replace_key (OstreeOrderedHash  *ohash,
+                             const char     *key,
+                             const char     *val)
+{
+  GString *buf;
+  gsize keylen;
+  char *valp;
+  char *valblock;
+  
+  buf = g_string_new (key);
+  keylen = buf->len;
+  g_string_append_c (buf, '\0');
+  g_string_append (buf, val);
+  valblock = g_string_free (buf, FALSE);
+  valp = valblock + keylen + 1;
+
+  _ostree_ordered_hash_replace_key_take (ohash, valblock, valp);
+}
diff --git a/src/libostree/ostree-ordered-hash.h b/src/libostree/ostree-ordered-hash.h
new file mode 100644 (file)
index 0000000..1cfe852
--- /dev/null
@@ -0,0 +1,44 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2013 Colin Walters <walters@verbum.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2 of the licence or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  GPtrArray  *order;
+  GHashTable *table;
+} OstreeOrderedHash;
+
+OstreeOrderedHash *_ostree_ordered_hash_new (void);
+void _ostree_ordered_hash_free (OstreeOrderedHash *ohash);
+void _ostree_ordered_hash_cleanup (void *loc);
+void _ostree_ordered_hash_replace_key_take (OstreeOrderedHash  *ohash,
+                                            char         *key,
+                                            const char   *value);
+void _ostree_ordered_hash_replace_key (OstreeOrderedHash  *ohash,
+                                       const char   *key,
+                                       const char   *val);
+
+
+G_END_DECLS
+
diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c
new file mode 100644 (file)
index 0000000..2b3e853
--- /dev/null
@@ -0,0 +1,1243 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Colin Walters <walters@verbum.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Colin Walters <walters@verbum.org>
+ */
+
+#include "config.h"
+
+#include "ostree-sysroot-private.h"
+#include "otutil.h"
+#include "libgsystem.h"
+
+/**
+ * copy_one_config_file:
+ *
+ * Copy @file from @modified_etc to @new_etc, overwriting any existing
+ * file there.
+ */
+static gboolean
+copy_one_config_file (GFile              *orig_etc,
+                      GFile              *modified_etc,
+                      GFile              *new_etc,
+                      GFile              *src,
+                      GCancellable       *cancellable,
+                      GError            **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFileInfo *src_info = NULL;
+  gs_unref_object GFile *dest = NULL;
+  gs_unref_object GFile *parent = NULL;
+  gs_free char *relative_path = NULL;
+  
+  relative_path = g_file_get_relative_path (modified_etc, src);
+  g_assert (relative_path);
+  dest = g_file_resolve_relative_path (new_etc, relative_path);
+
+  src_info = g_file_query_info (src, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                cancellable, error);
+  if (!src_info)
+    goto out;
+
+  if (g_file_info_get_file_type (src_info) == G_FILE_TYPE_DIRECTORY)
+    {
+      gs_unref_object GFileEnumerator *src_enum = NULL;
+      gs_unref_object GFileInfo *child_info = NULL;
+      GError *temp_error = NULL;
+
+      /* FIXME actually we need to copy permissions and xattrs */
+      if (!gs_file_ensure_directory (dest, TRUE, cancellable, error))
+        goto out;
+
+      src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
+                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                            cancellable, error);
+
+      while ((child_info = g_file_enumerator_next_file (src_enum, cancellable, error)) != NULL)
+        {
+          gs_unref_object GFile *child = g_file_get_child (src, g_file_info_get_name (child_info));
+
+          if (!copy_one_config_file (orig_etc, modified_etc, new_etc, child,
+                                     cancellable, error))
+            goto out;
+        }
+      g_clear_object (&child_info);
+      if (temp_error != NULL)
+        {
+          g_propagate_error (error, temp_error);
+          goto out;
+        }
+    }
+  else
+    {
+      parent = g_file_get_parent (dest);
+
+      /* FIXME actually we need to copy permissions and xattrs */
+      if (!gs_file_ensure_directory (parent, TRUE, cancellable, error))
+        goto out;
+      
+      /* We unlink here because otherwise gio throws an error on
+       * dangling symlinks.
+       */
+      if (!ot_gfile_ensure_unlinked (dest, cancellable, error))
+        goto out;
+
+      if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+                        cancellable, NULL, NULL, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * merge_etc_changes:
+ *
+ * Compute the difference between @orig_etc and @modified_etc,
+ * and apply that to @new_etc.
+ *
+ * The algorithm for computing the difference is pretty simple; it's
+ * approximately equivalent to "diff -unR orig_etc modified_etc",
+ * except that rather than attempting a 3-way merge if a file is also
+ * changed in @new_etc, the modified version always wins.
+ */
+static gboolean
+merge_etc_changes (GFile          *orig_etc,
+                   GFile          *modified_etc,
+                   GFile          *new_etc,
+                   GCancellable   *cancellable,
+                   GError        **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_ptrarray GPtrArray *modified = NULL;
+  gs_unref_ptrarray GPtrArray *removed = NULL;
+  gs_unref_ptrarray GPtrArray *added = NULL;
+  guint i;
+
+  modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
+  removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+  added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+
+  if (!ostree_diff_dirs (orig_etc, modified_etc, modified, removed, added,
+                         cancellable, error))
+    {
+      g_prefix_error (error, "While computing configuration diff: ");
+      goto out;
+    }
+
+  if (modified->len > 0 || removed->len > 0 || added->len > 0)
+    g_print ("ostadmin: Processing config: %u modified, %u removed, %u added\n", 
+             modified->len,
+             removed->len,
+             added->len);
+  else
+    g_print ("ostadmin: No modified configuration\n");
+
+  for (i = 0; i < removed->len; i++)
+    {
+      GFile *file = removed->pdata[i];
+      gs_unref_object GFile *target_file = NULL;
+      gs_free char *path = NULL;
+
+      path = g_file_get_relative_path (orig_etc, file);
+      g_assert (path);
+      target_file = g_file_resolve_relative_path (new_etc, path);
+
+      if (!ot_gfile_ensure_unlinked (target_file, cancellable, error))
+        goto out;
+    }
+
+  for (i = 0; i < modified->len; i++)
+    {
+      OstreeDiffItem *diff = modified->pdata[i];
+
+      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, diff->target,
+                                 cancellable, error))
+        goto out;
+    }
+  for (i = 0; i < added->len; i++)
+    {
+      GFile *file = added->pdata[i];
+
+      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, file,
+                                 cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * checkout_deployment_tree:
+ *
+ * Look up @revision in the repository, and check it out in
+ * /ostree/deploy/OS/deploy/${treecsum}.${deployserial}.
+ */
+static gboolean
+checkout_deployment_tree (OstreeSysroot     *sysroot,
+                          OstreeRepo        *repo,
+                          OstreeDeployment      *deployment,
+                          GFile            **out_deployment_path,
+                          GCancellable      *cancellable,
+                          GError           **error)
+{
+  gboolean ret = FALSE;
+  const char *csum = ostree_deployment_get_csum (deployment);
+  gs_unref_object GFile *root = NULL;
+  gs_unref_object GFileInfo *file_info = NULL;
+  gs_free char *checkout_target_name = NULL;
+  gs_unref_object GFile *osdeploy_path = NULL;
+  gs_unref_object GFile *deploy_target_path = NULL;
+  gs_unref_object GFile *deploy_parent = NULL;
+
+  if (!ostree_repo_read_commit (repo, csum, &root, NULL, cancellable, error))
+    goto out;
+
+  file_info = g_file_query_info (root, OSTREE_GIO_FAST_QUERYINFO,
+                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                 cancellable, error);
+  if (!file_info)
+    goto out;
+
+  osdeploy_path = ot_gfile_get_child_build_path (sysroot->path, "ostree", "deploy",
+                                                 ostree_deployment_get_osname (deployment),
+                                                 "deploy", NULL);
+  checkout_target_name = g_strdup_printf ("%s.%d", csum, ostree_deployment_get_deployserial (deployment));
+  deploy_target_path = g_file_get_child (osdeploy_path, checkout_target_name);
+
+  deploy_parent = g_file_get_parent (deploy_target_path);
+  if (!gs_file_ensure_directory (deploy_parent, TRUE, cancellable, error))
+    goto out;
+  
+  g_print ("ostadmin: Creating deployment %s\n",
+           gs_file_get_path_cached (deploy_target_path));
+
+  if (!ostree_repo_checkout_tree (repo, 0, 0, deploy_target_path, OSTREE_REPO_FILE (root),
+                                  file_info, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+  ot_transfer_out_value (out_deployment_path, &deploy_target_path);
+ out:
+  return ret;
+}
+
+static gboolean
+merge_configuration (OstreeSysroot         *sysroot,
+                     OstreeDeployment      *previous_deployment,
+                     OstreeDeployment      *deployment,
+                     GFile             *deployment_path,
+                     GCancellable      *cancellable,
+                     GError           **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *source_etc_path = NULL;
+  gs_unref_object GFile *source_etc_pristine_path = NULL;
+  gs_unref_object GFile *deployment_usretc_path = NULL;
+  gs_unref_object GFile *deployment_etc_path = NULL;
+  gboolean etc_exists;
+  gboolean usretc_exists;
+
+  if (previous_deployment)
+    {
+      gs_unref_object GFile *previous_path = NULL;
+      OstreeBootconfigParser *previous_bootconfig;
+
+      previous_path = ostree_sysroot_get_deployment_directory (sysroot, previous_deployment);
+      source_etc_path = g_file_resolve_relative_path (previous_path, "etc");
+      source_etc_pristine_path = g_file_resolve_relative_path (previous_path, "usr/etc");
+
+      previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment);
+      if (previous_bootconfig)
+        {
+          const char *previous_options = ostree_bootconfig_parser_get (previous_bootconfig, "options");
+          /* Completely overwrite the previous options here; we will extend
+           * them later.
+           */
+          ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options",
+                                        previous_options);
+        }
+    }
+
+  deployment_etc_path = g_file_get_child (deployment_path, "etc");
+  deployment_usretc_path = g_file_resolve_relative_path (deployment_path, "usr/etc");
+  
+  etc_exists = g_file_query_exists (deployment_etc_path, NULL);
+  usretc_exists = g_file_query_exists (deployment_usretc_path, NULL);
+
+  if (etc_exists && usretc_exists)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                           "Tree contains both /etc and /usr/etc");
+      goto out;
+    }
+  else if (etc_exists)
+    {
+      /* Compatibility hack */
+      if (!gs_file_rename (deployment_etc_path, deployment_usretc_path,
+                           cancellable, error))
+        goto out;
+      usretc_exists = TRUE;
+      etc_exists = FALSE;
+    }
+  
+  if (usretc_exists)
+    {
+      g_assert (!etc_exists);
+      if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
+                           cancellable, error))
+        goto out;
+      g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
+    }
+
+  if (source_etc_path)
+    {
+      if (!merge_etc_changes (source_etc_pristine_path, source_etc_path, deployment_etc_path, 
+                              cancellable, error))
+        goto out;
+    }
+  else
+    {
+      g_print ("ostadmin: No previous configuration changes to merge\n");
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+write_origin_file (OstreeSysroot         *sysroot,
+                   OstreeDeployment      *deployment,
+                   GCancellable      *cancellable,
+                   GError           **error)
+{
+  gboolean ret = FALSE;
+  GKeyFile *origin = ostree_deployment_get_origin (deployment);
+
+  if (origin)
+    {
+      gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (sysroot, deployment);
+      gs_unref_object GFile *origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
+      gs_free char *contents = NULL;
+      gsize len;
+
+      contents = g_key_file_to_data (origin, &len, error);
+      if (!contents)
+        goto out;
+
+      if (!g_file_replace_contents (origin_path, contents, len, NULL, FALSE,
+                                    G_FILE_CREATE_REPLACE_DESTINATION, NULL,
+                                    cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+get_kernel_from_tree (GFile         *deployroot,
+                      GFile        **out_kernel,
+                      GFile        **out_initramfs,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *bootdir = g_file_get_child (deployroot, "boot");
+  gs_unref_object GFileEnumerator *dir_enum = NULL;
+  gs_unref_object GFile *ret_kernel = NULL;
+  gs_unref_object GFile *ret_initramfs = NULL;
+  gs_free char *kernel_checksum = NULL;
+  gs_free char *initramfs_checksum = NULL;
+
+  dir_enum = g_file_enumerate_children (bootdir, OSTREE_GIO_FAST_QUERYINFO,
+                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                        NULL, error);
+  if (!dir_enum)
+    goto out;
+
+  while (TRUE)
+    {
+      GFileInfo *file_info = NULL;
+      const char *name;
+
+      if (!gs_file_enumerator_iterate (dir_enum, &file_info, NULL,
+                                       cancellable, error))
+        goto out;
+      if (file_info == NULL)
+        break;
+
+      name = g_file_info_get_name (file_info);
+      
+      if (ret_kernel == NULL && g_str_has_prefix (name, "vmlinuz-"))
+        {
+          const char *dash = strrchr (name, '-');
+          g_assert (dash);
+          if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
+            {
+              kernel_checksum = g_strdup (dash + 1);
+              ret_kernel = g_file_get_child (bootdir, name);
+            }
+        }
+      else if (ret_initramfs == NULL && g_str_has_prefix (name, "initramfs-"))
+        {
+          const char *dash = strrchr (name, '-');
+          g_assert (dash);
+          if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
+            {
+              initramfs_checksum = g_strdup (dash + 1);
+              ret_initramfs = g_file_get_child (bootdir, name);
+            }
+        }
+      
+      if (ret_kernel && ret_initramfs)
+        break;
+    }
+
+  if (ret_kernel == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                   "Failed to find boot/vmlinuz-<CHECKSUM> in %s",
+                   gs_file_get_path_cached (deployroot));
+      goto out;
+    }
+
+  if (ret_initramfs != NULL)
+    {
+      if (strcmp (kernel_checksum, initramfs_checksum) != 0)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                       "Mismatched kernel %s checksum vs initrd %s",
+                       gs_file_get_basename_cached (ret_initramfs),
+                       gs_file_get_basename_cached (ret_initramfs));
+          goto out;
+        }
+    }
+
+  ot_transfer_out_value (out_kernel, &ret_kernel);
+  ot_transfer_out_value (out_initramfs, &ret_initramfs);
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+checksum_from_kernel_src (GFile        *src,
+                          char        **out_checksum,
+                          GError     **error)
+{
+  const char *last_dash = strrchr (gs_file_get_path_cached (src), '-');
+  if (!last_dash)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Malformed initramfs name '%s', missing '-'", gs_file_get_basename_cached (src));
+      return FALSE;
+    }
+  *out_checksum = g_strdup (last_dash + 1);
+  return TRUE;
+}
+
+static int
+sort_by_bootserial (gconstpointer ap, gconstpointer bp)
+{
+  OstreeDeployment **a_loc = (OstreeDeployment**)ap;
+  OstreeDeployment *a = *a_loc;
+  OstreeDeployment **b_loc = (OstreeDeployment**)bp;
+  OstreeDeployment *b = *b_loc;
+
+  if (ostree_deployment_get_bootserial (a) == ostree_deployment_get_bootserial (b))
+    return 0;
+  else if (ostree_deployment_get_bootserial (a) < ostree_deployment_get_bootserial (b))
+    return -1;
+  return 1;
+}
+
+static GPtrArray *
+filter_deployments_by_bootcsum (GPtrArray    *deployments,
+                                const char   *osname,
+                                const char   *bootcsum)
+{
+  GPtrArray *ret = g_ptr_array_new ();
+  guint i;
+
+  for (i = 0; i < deployments->len; i++)
+    {
+      OstreeDeployment *deployment = deployments->pdata[i];
+      
+      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
+        continue;
+      if (strcmp (ostree_deployment_get_bootcsum (deployment), bootcsum) != 0)
+        continue;
+      
+      g_ptr_array_add (ret, deployment);
+    }
+  g_ptr_array_sort (ret, sort_by_bootserial);
+
+  return ret;
+}
+
+static void
+compute_new_deployment_list (int           current_bootversion,
+                             GPtrArray    *current_deployments,
+                             const char   *osname,
+                             OstreeDeployment *booted_deployment,
+                             OstreeDeployment *merge_deployment,
+                             gboolean      retain,
+                             const char   *revision,
+                             const char   *bootcsum,
+                             GPtrArray   **out_new_deployments,
+                             int          *out_new_bootversion)
+{
+  guint i;
+  int new_index;
+  guint new_deployserial = 0;
+  int new_bootserial = 0;
+  gs_unref_object OstreeDeployment *new_deployment = NULL;
+  gs_unref_ptrarray GPtrArray *matching_deployments_by_bootserial = NULL;
+  OstreeDeployment *deployment_to_delete = NULL;
+  gs_unref_ptrarray GPtrArray *ret_new_deployments = NULL;
+  gboolean requires_new_bootversion;
+
+  if (osname == NULL)
+    osname = ostree_deployment_get_osname (booted_deployment);
+
+  /* First, compute the serial for this deployment; we look
+   * for other ones in this os with the same checksum.
+   */
+  for (i = 0; i < current_deployments->len; i++)
+    {
+      OstreeDeployment *deployment = current_deployments->pdata[i];
+      
+      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
+        continue;
+      if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0)
+        continue;
+
+      new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1);
+    }
+
+  /* We retain by default (well, hardcoded now) one previous
+   * deployment for this OS, plus the booted deployment.  Usually, we
+   * have one previous, one into which we're booted, and we're
+   * deploying a new one.  So the old previous will get swapped out,
+   * and booted becomes previous.
+   *
+   * But if the user then upgrades again, we will end up pruning the
+   * front of the deployment list.  We never delete the running
+   * deployment.
+   */
+  if (!retain)
+    {
+      for (i = 0; i < current_deployments->len; i++)
+        {
+          OstreeDeployment *deployment = current_deployments->pdata[i];
+      
+          if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
+            continue;
+
+          // Keep both the booted and merge deployments
+          if (ostree_deployment_equal (deployment, booted_deployment) || 
+              ostree_deployment_equal (deployment, merge_deployment))
+            continue;
+
+          deployment_to_delete = deployment;
+        }
+    }
+
+  /* We need to update the bootloader only if the deployment we're
+   * removing uses a different kernel.
+   */
+  requires_new_bootversion =
+    (deployment_to_delete == NULL) ||
+    (strcmp (ostree_deployment_get_bootcsum (deployment_to_delete), bootcsum) != 0);
+
+  ret_new_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
+
+  new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial,
+                                      bootcsum, new_bootserial);
+  g_ptr_array_add (ret_new_deployments, g_object_ref (new_deployment));
+  new_index = 1;
+  for (i = 0; i < current_deployments->len; i++)
+    {
+      OstreeDeployment *orig_deployment = current_deployments->pdata[i];
+      gs_unref_object OstreeDeployment *deployment_clone = NULL;
+
+      if (orig_deployment == deployment_to_delete)
+        continue;
+
+      deployment_clone = ostree_deployment_clone (orig_deployment);
+      ostree_deployment_set_index (deployment_clone, new_index);
+      new_index++;
+      g_ptr_array_add (ret_new_deployments, g_object_ref (deployment_clone));
+    }
+
+  /* Just renumber the deployments for the OS we're adding; we don't
+   * handle anything else at the moment.
+   */
+  matching_deployments_by_bootserial = filter_deployments_by_bootcsum (ret_new_deployments,
+                                                                       osname, bootcsum);
+  for (i = 0; i < matching_deployments_by_bootserial->len; i++)
+    {
+      OstreeDeployment *deployment = matching_deployments_by_bootserial->pdata[i];
+      ostree_deployment_set_bootserial (deployment, i);
+    }
+
+  *out_new_deployments = ret_new_deployments;
+  ret_new_deployments = NULL;
+  g_assert (current_bootversion == 0 || current_bootversion == 1);
+  if (requires_new_bootversion)
+    *out_new_bootversion = (current_bootversion == 0) ? 1 : 0;
+  else
+    *out_new_bootversion = current_bootversion;
+}
+
+static GHashTable *
+object_array_to_set (GPtrArray   *objlist,
+                     GHashFunc    hashfunc,
+                     GEqualFunc   equalfunc)
+{
+  GHashTable *ret = g_hash_table_new_full (hashfunc, equalfunc, g_object_unref, NULL);
+  guint i;
+
+  for (i = 0; i < objlist->len; i++)
+    {
+      GObject *obj = g_object_ref (objlist->pdata[i]);
+      g_hash_table_insert (ret, obj, obj);
+    }
+  
+  return ret;
+}
+
+static GHashTable *
+object_set_subtract (GHashTable *a, GHashTable *b)
+{
+  GHashTable *ret = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
+  GHashTableIter hashiter;
+  gpointer hashkey, hashvalue;
+  
+  g_hash_table_iter_init (&hashiter, a);
+  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
+    {
+      if (!g_hash_table_contains (b, hashkey))
+        {
+          GObject *o = g_object_ref (hashkey);
+          g_hash_table_insert (ret, o, o);
+        }
+    }
+
+  return ret;
+}
+
+static void
+print_deployment_set (gboolean    for_removal,
+                      GHashTable *set)
+{
+  GHashTableIter hashiter;
+  gpointer hashkey, hashvalue;
+
+  if (g_hash_table_size (set) == 0)
+    return;
+
+  g_print ("%s\n", for_removal ? "removed:" : "added: ");
+
+  g_hash_table_iter_init (&hashiter, set);
+  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
+    {
+      OstreeDeployment *deployment = hashkey;
+
+      g_print ("  %c %s %s.%d",
+               for_removal ? '-' : '+', ostree_deployment_get_osname (deployment),
+               ostree_deployment_get_csum (deployment),
+               ostree_deployment_get_deployserial (deployment));
+
+      if (!for_removal)
+        g_print (" index=%d", ostree_deployment_get_index (deployment));
+      g_print ("\n");
+    }
+}
+
+static void
+print_deployment_diff (GPtrArray   *current_deployments,
+                       GPtrArray   *new_deployments)
+{
+  gs_unref_hashtable GHashTable *curset = object_array_to_set (current_deployments, ostree_deployment_hash, ostree_deployment_equal);
+  gs_unref_hashtable GHashTable *newset = object_array_to_set (new_deployments, ostree_deployment_hash, ostree_deployment_equal);
+  gs_unref_hashtable GHashTable *removed = NULL;
+  gs_unref_hashtable GHashTable *added = NULL;
+
+  removed = object_set_subtract (curset, newset);
+  added = object_set_subtract (newset, curset);
+
+  print_deployment_set (TRUE, removed);
+  print_deployment_set (FALSE, added);
+}
+
+/* FIXME: We should really do individual fdatasync() on files/dirs,
+ * since this causes us to block on unrelated I/O.  However, it's just
+ * safer for now.
+ */
+static gboolean
+full_system_sync (GCancellable      *cancellable,
+                  GError           **error)
+{
+  sync ();
+  return TRUE;
+}
+
+static gboolean
+swap_bootlinks (OstreeSysroot *sysroot,
+                int           current_bootversion,
+                GPtrArray    *new_deployments,
+                GCancellable *cancellable,
+                GError      **error)
+{
+  gboolean ret = FALSE;
+  guint i;
+  int old_subbootversion, new_subbootversion;
+  gs_unref_object GFile *ostree_dir = g_file_get_child (ostree_sysroot_get_path (sysroot), "ostree");
+  gs_free char *ostree_bootdir_name = g_strdup_printf ("boot.%d", current_bootversion);
+  gs_unref_object GFile *ostree_bootdir = g_file_resolve_relative_path (ostree_dir, ostree_bootdir_name);
+  gs_free char *ostree_subbootdir_name = NULL;
+  gs_unref_object GFile *ostree_subbootdir = NULL;
+
+  if (!ostree_sysroot_read_current_subbootversion (sysroot, current_bootversion,
+                                                   &old_subbootversion,
+                                                   cancellable, error))
+    goto out;
+
+  new_subbootversion = old_subbootversion == 0 ? 1 : 0;
+
+  ostree_subbootdir_name = g_strdup_printf ("boot.%d.%d", current_bootversion, new_subbootversion);
+  ostree_subbootdir = g_file_resolve_relative_path (ostree_dir, ostree_subbootdir_name);
+
+  if (!gs_file_ensure_directory (ostree_subbootdir, TRUE, cancellable, error))
+    goto out;
+
+  for (i = 0; i < new_deployments->len; i++)
+    {
+      OstreeDeployment *deployment = new_deployments->pdata[i];
+      gs_free char *bootlink_pathname = g_strdup_printf ("%s/%s/%d",
+                                                         ostree_deployment_get_osname (deployment),
+                                                         ostree_deployment_get_bootcsum (deployment),
+                                                         ostree_deployment_get_bootserial (deployment));
+      gs_free char *bootlink_target = g_strdup_printf ("../../../deploy/%s/deploy/%s.%d",
+                                                       ostree_deployment_get_osname (deployment),
+                                                       ostree_deployment_get_csum (deployment),
+                                                       ostree_deployment_get_deployserial (deployment));
+      gs_unref_object GFile *linkname = g_file_get_child (ostree_subbootdir, bootlink_pathname);
+      gs_unref_object GFile *linkname_parent = g_file_get_parent (linkname);
+
+      if (!gs_file_ensure_directory (linkname_parent, TRUE, cancellable, error))
+        goto out;
+
+      if (!g_file_make_symbolic_link (linkname, bootlink_target, cancellable, error))
+        goto out;
+    }
+
+  if (!ot_gfile_atomic_symlink_swap (ostree_bootdir, ostree_subbootdir_name,
+                                     cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static char *
+remove_checksum_from_kernel_name (const char *name,
+                                  const char *csum)
+{
+  const char *p = strrchr (name, '-');
+  g_assert_cmpstr (p+1, ==, csum);
+  return g_strndup (name, p-name);
+}
+
+static GHashTable *
+parse_os_release (const char *contents,
+                  const char *split)
+{
+  char **lines = g_strsplit (contents, split, -1);
+  char **iter;
+  GHashTable *ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+  for (iter = lines; *iter; iter++)
+    {
+      char *line = *iter;
+      char *eq;
+      const char *quotedval;
+      char *val;
+
+      if (g_str_has_prefix (line, "#"))
+        continue;
+      
+      eq = strchr (line, '=');
+      if (!eq)
+        continue;
+      
+      *eq = '\0';
+      quotedval = eq + 1;
+      val = g_shell_unquote (quotedval, NULL);
+      if (!val)
+        continue;
+      
+      g_hash_table_insert (ret, line, val);
+    }
+
+  return ret;
+}
+
+/*
+ * install_deployment_kernel:
+ * 
+ * Write out an entry in /boot/loader/entries for @deployment.
+ */
+static gboolean
+install_deployment_kernel (OstreeSysroot   *sysroot,
+                           int             new_bootversion,
+                           OstreeDeployment   *deployment,
+                           guint           n_deployments,
+                           GCancellable   *cancellable,
+                           GError        **error)
+
+{
+  gboolean ret = FALSE;
+  const char *osname = ostree_deployment_get_osname (deployment);
+  const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
+  gs_unref_object GFile *bootdir = NULL;
+  gs_unref_object GFile *bootcsumdir = NULL;
+  gs_unref_object GFile *bootconfpath = NULL;
+  gs_unref_object GFile *bootconfpath_parent = NULL;
+  gs_free char *dest_kernel_name = NULL;
+  gs_unref_object GFile *dest_kernel_path = NULL;
+  gs_unref_object GFile *dest_initramfs_path = NULL;
+  gs_unref_object GFile *tree_kernel_path = NULL;
+  gs_unref_object GFile *tree_initramfs_path = NULL;
+  gs_unref_object GFile *etc_os_release = NULL;
+  gs_unref_object GFile *deployment_dir = NULL;
+  gs_free char *contents = NULL;
+  gs_unref_hashtable GHashTable *osrelease_values = NULL;
+  gs_free char *linux_relpath = NULL;
+  gs_free char *linux_key = NULL;
+  gs_free char *initramfs_relpath = NULL;
+  gs_free char *title_key = NULL;
+  gs_free char *initrd_key = NULL;
+  gs_free char *version_key = NULL;
+  gs_free char *ostree_kernel_arg = NULL;
+  gs_free char *options_key = NULL;
+  __attribute__((cleanup(_ostree_ordered_hash_cleanup))) OstreeOrderedHash *ohash = NULL;
+  const char *val;
+  OstreeBootconfigParser *bootconfig;
+  gsize len;
+
+  bootconfig = ostree_deployment_get_bootconfig (deployment);
+  deployment_dir = ostree_sysroot_get_deployment_directory (sysroot, deployment);
+
+  if (!get_kernel_from_tree (deployment_dir, &tree_kernel_path, &tree_initramfs_path,
+                             cancellable, error))
+    goto out;
+
+  bootdir = g_file_get_child (ostree_sysroot_get_path (sysroot), "boot");
+  bootcsumdir = ot_gfile_resolve_path_printf (bootdir, "ostree/%s-%s",
+                                              osname,
+                                              bootcsum);
+  bootconfpath = ot_gfile_resolve_path_printf (bootdir, "loader.%d/entries/ostree-%s-%s-%d.conf",
+                                               new_bootversion, osname, 
+                                               ostree_deployment_get_csum (deployment),
+                                               ostree_deployment_get_bootserial (deployment));
+
+  if (!gs_file_ensure_directory (bootcsumdir, TRUE, cancellable, error))
+    goto out;
+  bootconfpath_parent = g_file_get_parent (bootconfpath);
+  if (!gs_file_ensure_directory (bootconfpath_parent, TRUE, cancellable, error))
+    goto out;
+
+  dest_kernel_name = remove_checksum_from_kernel_name (gs_file_get_basename_cached (tree_kernel_path),
+                                                       bootcsum);
+  dest_kernel_path = g_file_get_child (bootcsumdir, dest_kernel_name);
+  if (!g_file_query_exists (dest_kernel_path, NULL))
+    {
+      if (!gs_file_linkcopy_sync_data (tree_kernel_path, dest_kernel_path, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+                                       cancellable, error))
+        goto out;
+    }
+
+  if (tree_initramfs_path)
+    {
+      gs_free char *dest_initramfs_name = remove_checksum_from_kernel_name (gs_file_get_basename_cached (tree_initramfs_path),
+                                                                       bootcsum);
+      dest_initramfs_path = g_file_get_child (bootcsumdir, dest_initramfs_name);
+
+      if (!g_file_query_exists (dest_initramfs_path, NULL))
+        {
+          if (!gs_file_linkcopy_sync_data (tree_initramfs_path, dest_initramfs_path, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
+                                           cancellable, error))
+            goto out;
+        }
+    }
+
+  etc_os_release = g_file_resolve_relative_path (deployment_dir, "etc/os-release");
+
+  if (!g_file_load_contents (etc_os_release, cancellable,
+                             &contents, &len, NULL, error))
+    {
+      g_prefix_error (error, "Reading /etc/os-release: ");
+      goto out;
+    }
+
+  osrelease_values = parse_os_release (contents, "\n");
+
+  /* title */
+  val = g_hash_table_lookup (osrelease_values, "PRETTY_NAME");
+  if (val == NULL)
+      val = g_hash_table_lookup (osrelease_values, "ID");
+  if (val == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "No PRETTY_NAME or ID in /etc/os-release");
+      goto out;
+    }
+  
+  title_key = g_strdup_printf ("ostree:%s:%d %s", ostree_deployment_get_osname (deployment),
+                               ostree_deployment_get_index (deployment),
+                               val);
+  ostree_bootconfig_parser_set (bootconfig, "title", title_key);
+
+  version_key = g_strdup_printf ("%d", n_deployments - ostree_deployment_get_index (deployment));
+  ostree_bootconfig_parser_set (bootconfig, "version", version_key);
+
+  linux_relpath = g_file_get_relative_path (bootdir, dest_kernel_path);
+  linux_key = g_strconcat ("/", linux_relpath, NULL);
+  ostree_bootconfig_parser_set (bootconfig, "linux", linux_key);
+
+  if (dest_initramfs_path)
+    {
+      initramfs_relpath = g_file_get_relative_path (bootdir, dest_initramfs_path);
+      initrd_key = g_strconcat ("/", initramfs_relpath, NULL);
+      ostree_bootconfig_parser_set (bootconfig, "initrd", initrd_key);
+    }
+
+  val = ostree_bootconfig_parser_get (bootconfig, "options");
+  ostree_kernel_arg = g_strdup_printf ("/ostree/boot.%d/%s/%s/%d",
+                                       new_bootversion, osname, bootcsum,
+                                       ostree_deployment_get_bootserial (deployment));
+  ohash = _ostree_sysroot_parse_kernel_args (val);
+  _ostree_ordered_hash_replace_key (ohash, "ostree", ostree_kernel_arg);
+  options_key = _ostree_sysroot_kernel_arg_string_serialize (ohash);
+  ostree_bootconfig_parser_set (bootconfig, "options", options_key);
+  
+  if (!ostree_bootconfig_parser_write (ostree_deployment_get_bootconfig (deployment), bootconfpath,
+                               cancellable, error))
+      goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+swap_bootloader (OstreeSysroot  *sysroot,
+                 int             current_bootversion,
+                 int             new_bootversion,
+                 GCancellable   *cancellable,
+                 GError        **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *boot_loader_link = NULL;
+  gs_free char *new_target = NULL;
+
+  g_assert ((current_bootversion == 0 && new_bootversion == 1) ||
+            (current_bootversion == 1 && new_bootversion == 0));
+
+  boot_loader_link = g_file_resolve_relative_path (sysroot->path, "boot/loader");
+  new_target = g_strdup_printf ("loader.%d", new_bootversion);
+
+  if (!ot_gfile_atomic_symlink_swap (boot_loader_link, new_target,
+                                     cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+gboolean
+ostree_sysroot_write_deployments (OstreeSysroot     *sysroot,
+                                  int                current_bootversion,
+                                  int                new_bootversion,
+                                  GPtrArray         *new_deployments,
+                                  GCancellable      *cancellable,
+                                  GError           **error)
+{
+  gboolean ret = FALSE;
+  guint i;
+  gs_unref_object OstreeBootloader *bootloader = ostree_sysroot_query_bootloader (sysroot);
+
+  if (bootloader)
+    g_print ("Detected bootloader: %s\n", ostree_bootloader_get_name (bootloader));
+  else
+    g_print ("Detected bootloader: (unknown)\n");
+
+  if (current_bootversion == new_bootversion)
+    {
+      if (!full_system_sync (cancellable, error))
+        {
+          g_prefix_error (error, "Full sync: ");
+          goto out;
+        }
+
+      if (!swap_bootlinks (sysroot, current_bootversion,
+                           new_deployments,
+                           cancellable, error))
+        {
+          g_prefix_error (error, "Swapping current bootlinks: ");
+          goto out;
+        }
+    }
+  else
+    {
+      for (i = 0; i < new_deployments->len; i++)
+        {
+          OstreeDeployment *deployment = new_deployments->pdata[i];
+          if (!install_deployment_kernel (sysroot, new_bootversion,
+                                          deployment, new_deployments->len,
+                                          cancellable, error))
+            {
+              g_prefix_error (error, "Installing kernel: ");
+              goto out;
+            }
+        }
+
+      /* Swap bootlinks for *new* version */
+      if (!swap_bootlinks (sysroot, new_bootversion, new_deployments,
+                           cancellable, error))
+        {
+          g_prefix_error (error, "Generating new bootlinks: ");
+          goto out;
+        }
+
+      if (!full_system_sync (cancellable, error))
+        {
+          g_prefix_error (error, "Full sync: ");
+          goto out;
+        }
+
+      if (bootloader && !ostree_bootloader_write_config (bootloader, new_bootversion,
+                                                         cancellable, error))
+          {
+            g_prefix_error (error, "Bootloader write config: ");
+            goto out;
+          }
+
+      if (!swap_bootloader (sysroot, current_bootversion, new_bootversion,
+                            cancellable, error))
+        {
+          g_prefix_error (error, "Final bootloader swap: ");
+          goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+                            
+gboolean
+ostree_sysroot_deploy (OstreeSysroot     *sysroot,
+                       int                current_bootversion,
+                       GPtrArray         *current_deployments,
+                       const char        *osname,
+                       const char        *revision,
+                       GKeyFile          *origin,
+                       char             **add_kernel_argv,
+                       gboolean           retain,
+                       OstreeDeployment  *booted_deployment,
+                       OstreeDeployment  *provided_merge_deployment,
+                       OstreeDeployment **out_new_deployment,
+                       int               *out_new_bootversion,
+                       GPtrArray        **out_new_deployments,
+                       GCancellable      *cancellable,
+                       GError           **error)
+{
+  gboolean ret = FALSE;
+  OstreeDeployment *new_deployment;
+  gs_unref_object OstreeDeployment *merge_deployment = NULL;
+  gs_unref_object OstreeRepo *repo = NULL;
+  gs_unref_object GFile *commit_root = NULL;
+  gs_unref_object GFile *tree_kernel_path = NULL;
+  gs_unref_object GFile *tree_initramfs_path = NULL;
+  gs_unref_object GFile *new_deployment_path = NULL;
+  gs_free char *new_bootcsum = NULL;
+  gs_unref_object OstreeBootconfigParser *bootconfig = NULL;
+  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
+  int new_bootversion;
+
+  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
+    goto out;
+
+  /* Here we perform cleanup of any leftover data from previous
+   * partial failures.  This avoids having to call gs_shutil_rm_rf()
+   * at random points throughout the process.
+   *
+   * TODO: Add /ostree/transaction file, and only do this cleanup if
+   * we find it.
+   */
+  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+    {
+      g_prefix_error (error, "Performing initial cleanup: ");
+      goto out;
+    }
+
+  if (!ostree_repo_read_commit (repo, revision, &commit_root, NULL, cancellable, error))
+    goto out;
+
+  if (!get_kernel_from_tree (commit_root, &tree_kernel_path, &tree_initramfs_path,
+                             cancellable, error))
+    goto out;
+  
+  if (tree_initramfs_path != NULL)
+    {
+      if (!checksum_from_kernel_src (tree_initramfs_path, &new_bootcsum, error))
+        goto out;
+    }
+  else
+    {
+      if (!checksum_from_kernel_src (tree_kernel_path, &new_bootcsum, error))
+        goto out;
+    }
+
+  /* If we're booted into the OS into which we're deploying, then
+   * merge the currently *booted* configuration, rather than the most
+   * recently deployed.
+   */
+  if (provided_merge_deployment != NULL)
+    merge_deployment = g_object_ref (provided_merge_deployment);
+  else
+    merge_deployment = ostree_sysroot_get_merge_deployment (current_deployments, osname,
+                                                            booted_deployment); 
+
+  compute_new_deployment_list (current_bootversion,
+                               current_deployments, osname,
+                               booted_deployment, merge_deployment,
+                               retain,
+                               revision, new_bootcsum,
+                               &new_deployments,
+                               &new_bootversion);
+  new_deployment = g_object_ref (new_deployments->pdata[0]);
+  ostree_deployment_set_origin (new_deployment, origin);
+
+  print_deployment_diff (current_deployments, new_deployments);
+
+  /* Check out the userspace tree onto the filesystem */
+  if (!checkout_deployment_tree (sysroot, repo, new_deployment, &new_deployment_path,
+                                 cancellable, error))
+    {
+      g_prefix_error (error, "Checking out tree: ");
+      goto out;
+    }
+
+  if (!write_origin_file (sysroot, new_deployment, cancellable, error))
+    {
+      g_prefix_error (error, "Writing out origin file: ");
+      goto out;
+    }
+
+  /* Create an empty boot configuration; we will merge things into
+   * it as we go.
+   */
+  bootconfig = ostree_bootconfig_parser_new ();
+  ostree_deployment_set_bootconfig (new_deployment, bootconfig);
+
+  if (!merge_configuration (sysroot, merge_deployment, new_deployment,
+                            new_deployment_path,
+                            cancellable, error))
+    {
+      g_prefix_error (error, "During /etc merge: ");
+      goto out;
+    }
+
+  /* We have inherited kernel arguments from the previous deployment;
+   * now, override/extend that with arguments provided by the command
+   * line.
+   * 
+   * After this, install_deployment_kernel() will set the other boot
+   * options and write it out to disk.
+   */
+  if (add_kernel_argv)
+    {
+      char **strviter;
+      __attribute__((cleanup(_ostree_ordered_hash_cleanup))) OstreeOrderedHash *ohash = NULL;
+      gs_free char *new_options = NULL;
+
+      ohash = _ostree_sysroot_parse_kernel_args (ostree_bootconfig_parser_get (bootconfig, "options"));
+
+      for (strviter = add_kernel_argv; *strviter; strviter++)
+        {
+          char *karg = g_strdup (*strviter);
+          const char *val = _ostree_sysroot_split_keyeq (karg);
+        
+          _ostree_ordered_hash_replace_key_take (ohash, karg, val);
+        }
+
+      new_options = _ostree_sysroot_kernel_arg_string_serialize (ohash);
+      ostree_bootconfig_parser_set (bootconfig, "options", new_options);
+    }
+
+  if (!ostree_sysroot_write_deployments (sysroot, current_bootversion, new_bootversion,
+                                         new_deployments, cancellable, error))
+    goto out;
+
+  g_print ("Transaction complete, performing cleanup\n");
+
+  /* TEMPORARY HACK: Add a "current" symbolic link that's easy to
+   * follow inside the gnome-ostree build scripts.  This isn't atomic,
+   * but that doesn't matter because it's only used by deployments
+   * done from the host.
+   */
+  {
+    gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (sysroot->path, "ostree/deploy/%s", ostree_deployment_get_osname (new_deployment));
+    gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current");
+    gs_free char *target = g_file_get_relative_path (osdir, new_deployment_path);
+    g_assert (target != NULL);
+    if (!ot_gfile_atomic_symlink_swap (os_current_path, target,
+                                       cancellable, error))
+      goto out;
+  }
+
+  /* And finally, cleanup of any leftover data.
+   */
+  if (!ostree_sysroot_cleanup (sysroot, cancellable, error))
+    {
+      g_prefix_error (error, "Performing final cleanup: ");
+      goto out;
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_new_deployment, &new_deployment);
+  *out_new_bootversion = new_bootversion;
+  ot_transfer_out_value (out_new_deployments, &new_deployments)
+ out:
+  return ret;
+}
+
index 7da65072537d44562e385fe9de80c5a190b2b52c..f8b6d10c8f642029111b99d5bf1e0343bcf11348 100644 (file)
@@ -21,6 +21,7 @@
 #pragma once
 
 #include "ostree.h"
+#include "ostree-ordered-hash.h"
 
 G_BEGIN_DECLS
 
@@ -53,5 +54,11 @@ _ostree_sysroot_get_devino (GFile         *path,
 
 char *_ostree_sysroot_join_lines (GPtrArray  *lines);
 
+char *_ostree_sysroot_split_keyeq (char *str);
+
+OstreeOrderedHash *_ostree_sysroot_parse_kernel_args (const char *options);
+
+char * _ostree_sysroot_kernel_arg_string_serialize (OstreeOrderedHash *ohash);
+
 G_END_DECLS
 
index 71774deb50345fea1113d5b4f8168e3182d78895..464a42d706882cb1030284d4d9654f8cb36e179f 100644 (file)
@@ -839,3 +839,243 @@ _ostree_sysroot_join_lines (GPtrArray  *lines)
     }
   return g_string_free (buf, FALSE);
 }
+
+static gboolean
+parse_kernel_commandline (OstreeOrderedHash  **out_args,
+                          GCancellable    *cancellable,
+                          GError         **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *proc_cmdline = g_file_new_for_path ("/proc/cmdline");
+  gs_free char *contents = NULL;
+  gsize len;
+
+  if (!g_file_load_contents (proc_cmdline, cancellable, &contents, &len, NULL,
+                             error))
+    goto out;
+
+  ret = TRUE;
+  *out_args = _ostree_sysroot_parse_kernel_args (contents);;
+ out:
+  return ret;
+}
+
+/**
+ * ostree_sysroot_find_booted_deployment:
+ * @target_sysroot: Root directory
+ * @deployments: (element-type OstreeDeployment): Loaded deployments
+ * @out_deployment: (out): The currently booted deployment
+ * @cancellable:
+ * @error: 
+ * 
+ * If the system is currently booted into a deployment in
+ * @deployments, set @out_deployment.  Note that if @target_sysroot is
+ * not equal to "/", @out_deployment will always be set to %NULL.
+ */
+gboolean
+ostree_sysroot_find_booted_deployment (OstreeSysroot       *self,
+                                       GPtrArray           *deployments,
+                                       OstreeDeployment   **out_deployment,
+                                       GCancellable        *cancellable,
+                                       GError             **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object GFile *active_root = g_file_new_for_path ("/");
+  gs_unref_object OstreeSysroot *active_deployment_root = ostree_sysroot_new_default ();
+  gs_unref_object OstreeDeployment *ret_deployment = NULL;
+
+  if (g_file_equal (active_root, self->path))
+    { 
+      guint i;
+      const char *bootlink_arg;
+      __attribute__((cleanup(_ostree_ordered_hash_cleanup))) OstreeOrderedHash *kernel_args = NULL;
+      guint32 root_device;
+      guint64 root_inode;
+      
+      if (!_ostree_sysroot_get_devino (active_root, &root_device, &root_inode,
+                                       cancellable, error))
+        goto out;
+
+      if (!parse_kernel_commandline (&kernel_args, cancellable, error))
+        goto out;
+      
+      bootlink_arg = g_hash_table_lookup (kernel_args->table, "ostree");
+      if (bootlink_arg)
+        {
+          for (i = 0; i < deployments->len; i++)
+            {
+              OstreeDeployment *deployment = deployments->pdata[i];
+              gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (active_deployment_root, deployment);
+              guint32 device;
+              guint64 inode;
+
+              if (!_ostree_sysroot_get_devino (deployment_path, &device, &inode,
+                                               cancellable, error))
+                goto out;
+
+              if (device == root_device && inode == root_inode)
+                {
+                  ret_deployment = g_object_ref (deployment);
+                  break;
+                }
+            }
+          if (ret_deployment == NULL)
+            {
+              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Unexpected state: ostree= kernel argument found, but / is not a deployment root");
+              goto out;
+            }
+        }
+      else
+        {
+          /* Not an ostree system */
+        }
+    }
+
+  ret = TRUE;
+  ot_transfer_out_value (out_deployment, &ret_deployment);
+ out:
+  return ret;
+}
+
+gboolean
+ostree_sysroot_require_deployment_or_osname (OstreeSysroot       *sysroot,
+                                             GPtrArray           *deployments,
+                                             const char          *osname,
+                                             OstreeDeployment   **out_deployment,
+                                             GCancellable        *cancellable,
+                                             GError             **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object OstreeDeployment *ret_deployment = NULL;
+
+  if (!ostree_sysroot_find_booted_deployment (sysroot, deployments, &ret_deployment,
+                                              cancellable, error))
+    goto out;
+
+  if (ret_deployment == NULL && osname == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Not currently booted into an OSTree system and no --os= argument given");
+      goto out;
+    }
+  
+  ret = TRUE;
+  ot_transfer_out_value (out_deployment, &ret_deployment);
+ out:
+  return ret;
+}
+
+OstreeOrderedHash *
+_ostree_sysroot_parse_kernel_args (const char *options)
+{
+  OstreeOrderedHash *ret;
+  char **args;
+  char **iter;
+
+  ret = _ostree_ordered_hash_new ();
+
+  if (!options)
+    return ret;
+  
+  args = g_strsplit (options, " ", -1);
+  for (iter = args; *iter; iter++)
+    {
+      char *arg = *iter;
+      char *val;
+      
+      val = _ostree_sysroot_split_keyeq (arg);
+
+      g_ptr_array_add (ret->order, arg);
+      g_hash_table_insert (ret->table, arg, val);
+    }
+
+  return ret;
+}
+
+/*
+ * Modify @arg which should be of the form key=value to make @arg just
+ * contain key.  Return a pointer to the start of value.
+ */
+char *
+_ostree_sysroot_split_keyeq (char *arg)
+{
+  char *eq;
+      
+  eq = strchr (arg, '=');
+  if (eq)
+    {
+      /* Note key/val are in one malloc block,
+       * so we don't free val...
+       */
+      *eq = '\0';
+      return eq+1;
+    }
+  else
+    {
+      /* ...and this allows us to insert a constant
+       * string.
+       */
+      return "";
+    }
+}
+
+char *
+_ostree_sysroot_kernel_arg_string_serialize (OstreeOrderedHash *ohash)
+{
+  guint i;
+  GString *buf = g_string_new ("");
+  gboolean first = TRUE;
+
+  for (i = 0; i < ohash->order->len; i++)
+    {
+      const char *key = ohash->order->pdata[i];
+      const char *val = g_hash_table_lookup (ohash->table, key);
+
+      g_assert (val != NULL);
+
+      if (first)
+        first = FALSE;
+      else
+        g_string_append_c (buf, ' ');
+
+      if (*val)
+        g_string_append_printf (buf, "%s=%s", key, val);
+      else
+        g_string_append (buf, key);
+    }
+
+  return g_string_free (buf, FALSE);
+}
+
+OstreeDeployment *
+ostree_sysroot_get_merge_deployment (GPtrArray         *deployments,
+                                     const char        *osname,
+                                     OstreeDeployment  *booted_deployment)
+{
+  g_return_val_if_fail (osname != NULL || booted_deployment != NULL, NULL);
+
+  if (osname == NULL)
+    osname = ostree_deployment_get_osname (booted_deployment);
+
+  if (booted_deployment &&
+      g_strcmp0 (ostree_deployment_get_osname (booted_deployment), osname) == 0)
+    {
+      return g_object_ref (booted_deployment);
+    }
+  else
+    {
+      guint i;
+      for (i = 0; i < deployments->len; i++)
+        {
+          OstreeDeployment *deployment = deployments->pdata[i];
+
+          if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
+            continue;
+          
+          return g_object_ref (deployment);
+        }
+    }
+  return NULL;
+}
+
index 5df0ae706b64eaf239b7a9d7f3c497de63fef8b4..2227a638389f5c7829c2412e0b9b946a7c5bc2c2 100644 (file)
@@ -73,5 +73,46 @@ gboolean ostree_sysroot_get_repo (OstreeSysroot         *self,
 OstreeBootloader *ostree_sysroot_query_bootloader (OstreeSysroot         *sysroot);
 
 
+gboolean ostree_sysroot_find_booted_deployment (OstreeSysroot       *sysroot,
+                                                GPtrArray           *deployments,
+                                                OstreeDeployment   **out_deployment,
+                                                GCancellable        *cancellable,
+                                                GError             **error);
+
+gboolean ostree_sysroot_require_deployment_or_osname (OstreeSysroot       *sysroot,
+                                                      GPtrArray           *deployment_list,
+                                                      const char          *osname,
+                                                      OstreeDeployment   **out_deployment,
+                                                      GCancellable        *cancellable,
+                                                      GError             **error);
+
+gboolean ostree_sysroot_write_deployments (OstreeSysroot     *sysroot,
+                                           int                current_bootversion,
+                                           int                new_bootversion,
+                                           GPtrArray         *new_deployments,
+                                           GCancellable      *cancellable,
+                                           GError           **error);
+
+gboolean ostree_sysroot_deploy (OstreeSysroot     *sysroot,
+                                int                current_bootversion,
+                                GPtrArray         *current_deployments,
+                                const char        *osname,
+                                const char        *revision,
+                                GKeyFile          *origin,
+                                char             **add_kernel_argv,
+                                gboolean           retain,
+                                OstreeDeployment  *booted_deployment,
+                                OstreeDeployment  *merge_deployment,
+                                OstreeDeployment **out_new_deployment,
+                                int               *out_new_bootversion,
+                                GPtrArray        **out_new_deployments,
+                                GCancellable      *cancellable,
+                                GError           **error);
+
+
+OstreeDeployment *ostree_sysroot_get_merge_deployment (GPtrArray         *deployment_list,
+                                                       const char        *osname,
+                                                       OstreeDeployment  *booted_deployment);
+
 G_END_DECLS
 
index 883f77affe4de3867606b947a1627ca1c50dec10..6e5cc6013c8f6ccdcb3bf6e4d32c7d8fffb42238 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "ot-admin-builtins.h"
 #include "ot-admin-functions.h"
-#include "ot-admin-deploy.h"
-#include "ot-ordered-hash.h"
 #include "ostree.h"
 #include "otutil.h"
 
@@ -90,10 +88,10 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   /* Find the currently booted deployment, if any; we will ensure it
    * is present in the new deployment list.
    */
-  if (!ot_admin_require_deployment_or_osname (ostree_sysroot_get_path (sysroot), current_deployments,
-                                              opt_osname,
-                                              &booted_deployment,
-                                              cancellable, error))
+  if (!ostree_sysroot_require_deployment_or_osname (sysroot, current_deployments,
+                                                    opt_osname,
+                                                    &booted_deployment,
+                                                    cancellable, error))
     {
       g_prefix_error (error, "Looking for booted deployment: ");
       goto out;
@@ -114,12 +112,12 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error))
     goto out;
 
-  if (!ot_admin_deploy (ostree_sysroot_get_path (sysroot), current_bootversion, current_deployments,
-                        opt_osname, revision, origin,
-                        opt_kernel_argv, opt_retain,
-                        booted_deployment, NULL,
-                        &new_deployment, &new_bootversion, &new_deployments,
-                        cancellable, error))
+  if (!ostree_sysroot_deploy (sysroot, current_bootversion, current_deployments,
+                              opt_osname, revision, origin,
+                              opt_kernel_argv, opt_retain,
+                              booted_deployment, NULL,
+                              &new_deployment, &new_bootversion, &new_deployments,
+                              cancellable, error))
     goto out;
 
   ret = TRUE;
index 802fb17725d19be9139e11b7723a4fedb53d8afb..88e3717ec39bb9beb84cb4cf53eb859642415b73 100644 (file)
@@ -68,14 +68,14 @@ ot_admin_builtin_diff (int argc, char **argv, OstreeSysroot *sysroot, GCancellab
       goto out;
     }
 
-  if (!ot_admin_require_deployment_or_osname (ostree_sysroot_get_path (sysroot), deployments,
-                                              opt_osname, &deployment,
-                                              cancellable, error))
+  if (!ostree_sysroot_require_deployment_or_osname (sysroot, deployments,
+                                                    opt_osname, &deployment,
+                                                    cancellable, error))
     goto out;
   if (deployment != NULL)
     opt_osname = (char*)ostree_deployment_get_osname (deployment);
   if (deployment == NULL)
-    deployment = ot_admin_get_merge_deployment (deployments, opt_osname, deployment);
+    deployment = ostree_sysroot_get_merge_deployment (deployments, opt_osname, deployment);
   if (deployment == NULL)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
index cdddf114ea020964357ecde374d65a30052002d8..c561eaf098ee90730eff5267c9d5db72612302f3 100644 (file)
@@ -57,9 +57,9 @@ ot_admin_builtin_status (int argc, char **argv, OstreeSysroot *sysroot, GCancell
       goto out;
     }
 
-  if (!ot_admin_find_booted_deployment (ostree_sysroot_get_path (sysroot), deployments,
-                                        &booted_deployment,
-                                        cancellable, error))
+  if (!ostree_sysroot_find_booted_deployment (sysroot, deployments,
+                                              &booted_deployment,
+                                              cancellable, error))
     goto out;
 
   if (deployments->len == 0)
index e1535a521b2eff93002b4f1ba889ee6ed3f9eba5..1de2a6c24c62e977608cc03a228abfbf8740e684 100644 (file)
@@ -24,8 +24,6 @@
 
 #include "ot-admin-builtins.h"
 #include "ot-admin-functions.h"
-#include "ot-admin-deploy.h"
-#include "ot-ordered-hash.h"
 #include "ostree.h"
 #include "otutil.h"
 
@@ -68,8 +66,8 @@ ot_admin_builtin_undeploy (int argc, char **argv, OstreeSysroot *sysroot, GCance
       goto out;
     }
 
-  if (!ot_admin_find_booted_deployment (ostree_sysroot_get_path (sysroot), current_deployments, &booted_deployment,
-                                        cancellable, error))
+  if (!ostree_sysroot_find_booted_deployment (sysroot, current_deployments, &booted_deployment,
+                                              cancellable, error))
     goto out;
 
   if (deploy_index < 0)
@@ -95,9 +93,9 @@ ot_admin_builtin_undeploy (int argc, char **argv, OstreeSysroot *sysroot, GCance
   
   g_ptr_array_remove_index (current_deployments, deploy_index);
 
-  if (!ot_admin_write_deployments (ostree_sysroot_get_path (sysroot), current_bootversion,
-                                   current_bootversion ? 0 : 1, current_deployments,
-                                   cancellable, error))
+  if (!ostree_sysroot_write_deployments (sysroot, current_bootversion,
+                                         current_bootversion ? 0 : 1, current_deployments,
+                                         cancellable, error))
     goto out;
 
   g_print ("Deleted deployment %s.%d\n", ostree_deployment_get_csum (target_deployment),
index 063703d4dee864855282e2d7f78e0d670ba837d9..70204006d234d9f2da3555a117586add9acf2daa 100644 (file)
@@ -24,7 +24,6 @@
 
 #include "ot-admin-builtins.h"
 #include "ot-admin-functions.h"
-#include "ot-admin-deploy.h"
 #include "ostree.h"
 #include "otutil.h"
 #include "libgsystem.h"
@@ -78,15 +77,15 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
       goto out;
     }
 
-  if (!ot_admin_require_deployment_or_osname (ostree_sysroot_get_path (sysroot), current_deployments,
-                                              opt_osname,
-                                              &booted_deployment,
-                                              cancellable, error))
+  if (!ostree_sysroot_require_deployment_or_osname (sysroot, current_deployments,
+                                                    opt_osname,
+                                                    &booted_deployment,
+                                                    cancellable, error))
     goto out;
   if (!opt_osname)
     opt_osname = (char*)ostree_deployment_get_osname (booted_deployment);
-  merge_deployment = ot_admin_get_merge_deployment (current_deployments, opt_osname,
-                                                    booted_deployment);
+  merge_deployment = ostree_sysroot_get_merge_deployment (current_deployments, opt_osname,
+                                                          booted_deployment);
 
   deployment_path = ostree_sysroot_get_deployment_directory (sysroot, merge_deployment);
   deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
@@ -135,13 +134,13 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
   else
     {
       gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/");
-      if (!ot_admin_deploy (ostree_sysroot_get_path (sysroot),
-                            current_bootversion, current_deployments,
-                            opt_osname, new_revision, origin,
-                            NULL, FALSE,
-                            booted_deployment, merge_deployment,
-                            &new_deployment, &new_bootversion, &new_deployments,
-                            cancellable, error))
+      if (!ostree_sysroot_deploy (sysroot,
+                                  current_bootversion, current_deployments,
+                                  opt_osname, new_revision, origin,
+                                  NULL, FALSE,
+                                  booted_deployment, merge_deployment,
+                                  &new_deployment, &new_bootversion, &new_deployments,
+                                  cancellable, error))
         goto out;
 
       if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot))
diff --git a/src/ostree/ot-admin-deploy.c b/src/ostree/ot-admin-deploy.c
deleted file mode 100644 (file)
index 32d0d95..0000000
+++ /dev/null
@@ -1,1253 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2012 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-admin-functions.h"
-#include "ot-admin-deploy.h"
-#include "otutil.h"
-#include "ostree-core.h"
-#include "libgsystem.h"
-
-
-/**
- * copy_one_config_file:
- *
- * Copy @file from @modified_etc to @new_etc, overwriting any existing
- * file there.
- */
-static gboolean
-copy_one_config_file (GFile              *orig_etc,
-                      GFile              *modified_etc,
-                      GFile              *new_etc,
-                      GFile              *src,
-                      GCancellable       *cancellable,
-                      GError            **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFileInfo *src_info = NULL;
-  gs_unref_object GFile *dest = NULL;
-  gs_unref_object GFile *parent = NULL;
-  gs_free char *relative_path = NULL;
-  
-  relative_path = g_file_get_relative_path (modified_etc, src);
-  g_assert (relative_path);
-  dest = g_file_resolve_relative_path (new_etc, relative_path);
-
-  src_info = g_file_query_info (src, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                cancellable, error);
-  if (!src_info)
-    goto out;
-
-  if (g_file_info_get_file_type (src_info) == G_FILE_TYPE_DIRECTORY)
-    {
-      gs_unref_object GFileEnumerator *src_enum = NULL;
-      gs_unref_object GFileInfo *child_info = NULL;
-      GError *temp_error = NULL;
-
-      /* FIXME actually we need to copy permissions and xattrs */
-      if (!gs_file_ensure_directory (dest, TRUE, cancellable, error))
-        goto out;
-
-      src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
-                                            G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                            cancellable, error);
-
-      while ((child_info = g_file_enumerator_next_file (src_enum, cancellable, error)) != NULL)
-        {
-          gs_unref_object GFile *child = g_file_get_child (src, g_file_info_get_name (child_info));
-
-          if (!copy_one_config_file (orig_etc, modified_etc, new_etc, child,
-                                     cancellable, error))
-            goto out;
-        }
-      g_clear_object (&child_info);
-      if (temp_error != NULL)
-        {
-          g_propagate_error (error, temp_error);
-          goto out;
-        }
-    }
-  else
-    {
-      parent = g_file_get_parent (dest);
-
-      /* FIXME actually we need to copy permissions and xattrs */
-      if (!gs_file_ensure_directory (parent, TRUE, cancellable, error))
-        goto out;
-      
-      /* We unlink here because otherwise gio throws an error on
-       * dangling symlinks.
-       */
-      if (!ot_gfile_ensure_unlinked (dest, cancellable, error))
-        goto out;
-
-      if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
-                        cancellable, NULL, NULL, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * merge_etc_changes:
- *
- * Compute the difference between @orig_etc and @modified_etc,
- * and apply that to @new_etc.
- *
- * The algorithm for computing the difference is pretty simple; it's
- * approximately equivalent to "diff -unR orig_etc modified_etc",
- * except that rather than attempting a 3-way merge if a file is also
- * changed in @new_etc, the modified version always wins.
- */
-static gboolean
-merge_etc_changes (GFile          *orig_etc,
-                   GFile          *modified_etc,
-                   GFile          *new_etc,
-                   GCancellable   *cancellable,
-                   GError        **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_ptrarray GPtrArray *modified = NULL;
-  gs_unref_ptrarray GPtrArray *removed = NULL;
-  gs_unref_ptrarray GPtrArray *added = NULL;
-  guint i;
-
-  modified = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_diff_item_unref);
-  removed = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
-  added = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
-
-  if (!ostree_diff_dirs (orig_etc, modified_etc, modified, removed, added,
-                         cancellable, error))
-    {
-      g_prefix_error (error, "While computing configuration diff: ");
-      goto out;
-    }
-
-  if (modified->len > 0 || removed->len > 0 || added->len > 0)
-    g_print ("ostadmin: Processing config: %u modified, %u removed, %u added\n", 
-             modified->len,
-             removed->len,
-             added->len);
-  else
-    g_print ("ostadmin: No modified configuration\n");
-
-  for (i = 0; i < removed->len; i++)
-    {
-      GFile *file = removed->pdata[i];
-      gs_unref_object GFile *target_file = NULL;
-      gs_free char *path = NULL;
-
-      path = g_file_get_relative_path (orig_etc, file);
-      g_assert (path);
-      target_file = g_file_resolve_relative_path (new_etc, path);
-
-      if (!ot_gfile_ensure_unlinked (target_file, cancellable, error))
-        goto out;
-    }
-
-  for (i = 0; i < modified->len; i++)
-    {
-      OstreeDiffItem *diff = modified->pdata[i];
-
-      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, diff->target,
-                                 cancellable, error))
-        goto out;
-    }
-  for (i = 0; i < added->len; i++)
-    {
-      GFile *file = added->pdata[i];
-
-      if (!copy_one_config_file (orig_etc, modified_etc, new_etc, file,
-                                 cancellable, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-/**
- * checkout_deployment_tree:
- *
- * Look up @revision in the repository, and check it out in
- * /ostree/deploy/OS/deploy/${treecsum}.${deployserial}.
- */
-static gboolean
-checkout_deployment_tree (GFile             *sysroot,
-                          OstreeRepo        *repo,
-                          OstreeDeployment      *deployment,
-                          GFile            **out_deployment_path,
-                          GCancellable      *cancellable,
-                          GError           **error)
-{
-  gboolean ret = FALSE;
-  const char *csum = ostree_deployment_get_csum (deployment);
-  gs_unref_object GFile *root = NULL;
-  gs_unref_object GFileInfo *file_info = NULL;
-  gs_free char *checkout_target_name = NULL;
-  gs_unref_object GFile *osdeploy_path = NULL;
-  gs_unref_object GFile *deploy_target_path = NULL;
-  gs_unref_object GFile *deploy_parent = NULL;
-
-  if (!ostree_repo_read_commit (repo, csum, &root, NULL, cancellable, error))
-    goto out;
-
-  file_info = g_file_query_info (root, OSTREE_GIO_FAST_QUERYINFO,
-                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                 cancellable, error);
-  if (!file_info)
-    goto out;
-
-  osdeploy_path = ot_gfile_get_child_build_path (sysroot, "ostree", "deploy",
-                                                 ostree_deployment_get_osname (deployment),
-                                                 "deploy", NULL);
-  checkout_target_name = g_strdup_printf ("%s.%d", csum, ostree_deployment_get_deployserial (deployment));
-  deploy_target_path = g_file_get_child (osdeploy_path, checkout_target_name);
-
-  deploy_parent = g_file_get_parent (deploy_target_path);
-  if (!gs_file_ensure_directory (deploy_parent, TRUE, cancellable, error))
-    goto out;
-  
-  g_print ("ostadmin: Creating deployment %s\n",
-           gs_file_get_path_cached (deploy_target_path));
-
-  if (!ostree_repo_checkout_tree (repo, 0, 0, deploy_target_path, OSTREE_REPO_FILE (root),
-                                  file_info, cancellable, error))
-    goto out;
-
-  ret = TRUE;
-  ot_transfer_out_value (out_deployment_path, &deploy_target_path);
- out:
-  return ret;
-}
-
-static gboolean
-merge_configuration (OstreeSysroot         *sysroot_obj,
-                     OstreeDeployment      *previous_deployment,
-                     OstreeDeployment      *deployment,
-                     GFile             *deployment_path,
-                     GCancellable      *cancellable,
-                     GError           **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *source_etc_path = NULL;
-  gs_unref_object GFile *source_etc_pristine_path = NULL;
-  gs_unref_object GFile *deployment_usretc_path = NULL;
-  gs_unref_object GFile *deployment_etc_path = NULL;
-  gboolean etc_exists;
-  gboolean usretc_exists;
-
-  if (previous_deployment)
-    {
-      gs_unref_object GFile *previous_path = NULL;
-      OstreeBootconfigParser *previous_bootconfig;
-
-      previous_path = ostree_sysroot_get_deployment_directory (sysroot_obj, previous_deployment);
-      source_etc_path = g_file_resolve_relative_path (previous_path, "etc");
-      source_etc_pristine_path = g_file_resolve_relative_path (previous_path, "usr/etc");
-
-      previous_bootconfig = ostree_deployment_get_bootconfig (previous_deployment);
-      if (previous_bootconfig)
-        {
-          const char *previous_options = ostree_bootconfig_parser_get (previous_bootconfig, "options");
-          /* Completely overwrite the previous options here; we will extend
-           * them later.
-           */
-          ostree_bootconfig_parser_set (ostree_deployment_get_bootconfig (deployment), "options",
-                                        previous_options);
-        }
-    }
-
-  deployment_etc_path = g_file_get_child (deployment_path, "etc");
-  deployment_usretc_path = g_file_resolve_relative_path (deployment_path, "usr/etc");
-  
-  etc_exists = g_file_query_exists (deployment_etc_path, NULL);
-  usretc_exists = g_file_query_exists (deployment_usretc_path, NULL);
-
-  if (etc_exists && usretc_exists)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                           "Tree contains both /etc and /usr/etc");
-      goto out;
-    }
-  else if (etc_exists)
-    {
-      /* Compatibility hack */
-      if (!gs_file_rename (deployment_etc_path, deployment_usretc_path,
-                           cancellable, error))
-        goto out;
-      usretc_exists = TRUE;
-      etc_exists = FALSE;
-    }
-  
-  if (usretc_exists)
-    {
-      g_assert (!etc_exists);
-      if (!gs_shutil_cp_a (deployment_usretc_path, deployment_etc_path,
-                           cancellable, error))
-        goto out;
-      g_print ("ostadmin: Created %s\n", gs_file_get_path_cached (deployment_etc_path));
-    }
-
-  if (source_etc_path)
-    {
-      if (!merge_etc_changes (source_etc_pristine_path, source_etc_path, deployment_etc_path, 
-                              cancellable, error))
-        goto out;
-    }
-  else
-    {
-      g_print ("ostadmin: No previous configuration changes to merge\n");
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-write_origin_file (GFile             *sysroot,
-                   OstreeDeployment      *deployment,
-                   GCancellable      *cancellable,
-                   GError           **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object OstreeSysroot *sysroot_obj = NULL;
-  GKeyFile *origin = ostree_deployment_get_origin (deployment);
-
-  sysroot_obj = ostree_sysroot_new (sysroot);
-
-  if (origin)
-    {
-      gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (sysroot_obj, deployment);
-      gs_unref_object GFile *origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
-      gs_free char *contents = NULL;
-      gsize len;
-
-      contents = g_key_file_to_data (origin, &len, error);
-      if (!contents)
-        goto out;
-
-      if (!g_file_replace_contents (origin_path, contents, len, NULL, FALSE,
-                                    G_FILE_CREATE_REPLACE_DESTINATION, NULL,
-                                    cancellable, error))
-        goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-get_kernel_from_tree (GFile         *deployroot,
-                      GFile        **out_kernel,
-                      GFile        **out_initramfs,
-                      GCancellable  *cancellable,
-                      GError       **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *bootdir = g_file_get_child (deployroot, "boot");
-  gs_unref_object GFileEnumerator *dir_enum = NULL;
-  gs_unref_object GFile *ret_kernel = NULL;
-  gs_unref_object GFile *ret_initramfs = NULL;
-  gs_free char *kernel_checksum = NULL;
-  gs_free char *initramfs_checksum = NULL;
-
-  dir_enum = g_file_enumerate_children (bootdir, OSTREE_GIO_FAST_QUERYINFO,
-                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                        NULL, error);
-  if (!dir_enum)
-    goto out;
-
-  while (TRUE)
-    {
-      GFileInfo *file_info = NULL;
-      const char *name;
-
-      if (!gs_file_enumerator_iterate (dir_enum, &file_info, NULL,
-                                       cancellable, error))
-        goto out;
-      if (file_info == NULL)
-        break;
-
-      name = g_file_info_get_name (file_info);
-      
-      if (ret_kernel == NULL && g_str_has_prefix (name, "vmlinuz-"))
-        {
-          const char *dash = strrchr (name, '-');
-          g_assert (dash);
-          if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
-            {
-              kernel_checksum = g_strdup (dash + 1);
-              ret_kernel = g_file_get_child (bootdir, name);
-            }
-        }
-      else if (ret_initramfs == NULL && g_str_has_prefix (name, "initramfs-"))
-        {
-          const char *dash = strrchr (name, '-');
-          g_assert (dash);
-          if (ostree_validate_structureof_checksum_string (dash + 1, NULL))
-            {
-              initramfs_checksum = g_strdup (dash + 1);
-              ret_initramfs = g_file_get_child (bootdir, name);
-            }
-        }
-      
-      if (ret_kernel && ret_initramfs)
-        break;
-    }
-
-  if (ret_kernel == NULL)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                   "Failed to find boot/vmlinuz-<CHECKSUM> in %s",
-                   gs_file_get_path_cached (deployroot));
-      goto out;
-    }
-
-  if (ret_initramfs != NULL)
-    {
-      if (strcmp (kernel_checksum, initramfs_checksum) != 0)
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                       "Mismatched kernel %s checksum vs initrd %s",
-                       gs_file_get_basename_cached (ret_initramfs),
-                       gs_file_get_basename_cached (ret_initramfs));
-          goto out;
-        }
-    }
-
-  ot_transfer_out_value (out_kernel, &ret_kernel);
-  ot_transfer_out_value (out_initramfs, &ret_initramfs);
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-checksum_from_kernel_src (GFile        *src,
-                          char        **out_checksum,
-                          GError     **error)
-{
-  const char *last_dash = strrchr (gs_file_get_path_cached (src), '-');
-  if (!last_dash)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Malformed initramfs name '%s', missing '-'", gs_file_get_basename_cached (src));
-      return FALSE;
-    }
-  *out_checksum = g_strdup (last_dash + 1);
-  return TRUE;
-}
-
-static int
-sort_by_bootserial (gconstpointer ap, gconstpointer bp)
-{
-  OstreeDeployment **a_loc = (OstreeDeployment**)ap;
-  OstreeDeployment *a = *a_loc;
-  OstreeDeployment **b_loc = (OstreeDeployment**)bp;
-  OstreeDeployment *b = *b_loc;
-
-  if (ostree_deployment_get_bootserial (a) == ostree_deployment_get_bootserial (b))
-    return 0;
-  else if (ostree_deployment_get_bootserial (a) < ostree_deployment_get_bootserial (b))
-    return -1;
-  return 1;
-}
-
-static GPtrArray *
-filter_deployments_by_bootcsum (GPtrArray    *deployments,
-                                const char   *osname,
-                                const char   *bootcsum)
-{
-  GPtrArray *ret = g_ptr_array_new ();
-  guint i;
-
-  for (i = 0; i < deployments->len; i++)
-    {
-      OstreeDeployment *deployment = deployments->pdata[i];
-      
-      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-        continue;
-      if (strcmp (ostree_deployment_get_bootcsum (deployment), bootcsum) != 0)
-        continue;
-      
-      g_ptr_array_add (ret, deployment);
-    }
-  g_ptr_array_sort (ret, sort_by_bootserial);
-
-  return ret;
-}
-
-static void
-compute_new_deployment_list (int           current_bootversion,
-                             GPtrArray    *current_deployments,
-                             const char   *osname,
-                             OstreeDeployment *booted_deployment,
-                             OstreeDeployment *merge_deployment,
-                             gboolean      retain,
-                             const char   *revision,
-                             const char   *bootcsum,
-                             GPtrArray   **out_new_deployments,
-                             int          *out_new_bootversion)
-{
-  guint i;
-  int new_index;
-  guint new_deployserial = 0;
-  int new_bootserial = 0;
-  gs_unref_object OstreeDeployment *new_deployment = NULL;
-  gs_unref_ptrarray GPtrArray *matching_deployments_by_bootserial = NULL;
-  OstreeDeployment *deployment_to_delete = NULL;
-  gs_unref_ptrarray GPtrArray *ret_new_deployments = NULL;
-  gboolean requires_new_bootversion;
-
-  if (osname == NULL)
-    osname = ostree_deployment_get_osname (booted_deployment);
-
-  /* First, compute the serial for this deployment; we look
-   * for other ones in this os with the same checksum.
-   */
-  for (i = 0; i < current_deployments->len; i++)
-    {
-      OstreeDeployment *deployment = current_deployments->pdata[i];
-      
-      if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-        continue;
-      if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0)
-        continue;
-
-      new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1);
-    }
-
-  /* We retain by default (well, hardcoded now) one previous
-   * deployment for this OS, plus the booted deployment.  Usually, we
-   * have one previous, one into which we're booted, and we're
-   * deploying a new one.  So the old previous will get swapped out,
-   * and booted becomes previous.
-   *
-   * But if the user then upgrades again, we will end up pruning the
-   * front of the deployment list.  We never delete the running
-   * deployment.
-   */
-  if (!retain)
-    {
-      for (i = 0; i < current_deployments->len; i++)
-        {
-          OstreeDeployment *deployment = current_deployments->pdata[i];
-      
-          if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-            continue;
-
-          // Keep both the booted and merge deployments
-          if (ostree_deployment_equal (deployment, booted_deployment) || 
-              ostree_deployment_equal (deployment, merge_deployment))
-            continue;
-
-          deployment_to_delete = deployment;
-        }
-    }
-
-  /* We need to update the bootloader only if the deployment we're
-   * removing uses a different kernel.
-   */
-  requires_new_bootversion =
-    (deployment_to_delete == NULL) ||
-    (strcmp (ostree_deployment_get_bootcsum (deployment_to_delete), bootcsum) != 0);
-
-  ret_new_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref);
-
-  new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial,
-                                      bootcsum, new_bootserial);
-  g_ptr_array_add (ret_new_deployments, g_object_ref (new_deployment));
-  new_index = 1;
-  for (i = 0; i < current_deployments->len; i++)
-    {
-      OstreeDeployment *orig_deployment = current_deployments->pdata[i];
-      gs_unref_object OstreeDeployment *deployment_clone = NULL;
-
-      if (orig_deployment == deployment_to_delete)
-        continue;
-
-      deployment_clone = ostree_deployment_clone (orig_deployment);
-      ostree_deployment_set_index (deployment_clone, new_index);
-      new_index++;
-      g_ptr_array_add (ret_new_deployments, g_object_ref (deployment_clone));
-    }
-
-  /* Just renumber the deployments for the OS we're adding; we don't
-   * handle anything else at the moment.
-   */
-  matching_deployments_by_bootserial = filter_deployments_by_bootcsum (ret_new_deployments,
-                                                                       osname, bootcsum);
-  for (i = 0; i < matching_deployments_by_bootserial->len; i++)
-    {
-      OstreeDeployment *deployment = matching_deployments_by_bootserial->pdata[i];
-      ostree_deployment_set_bootserial (deployment, i);
-    }
-
-  *out_new_deployments = ret_new_deployments;
-  ret_new_deployments = NULL;
-  g_assert (current_bootversion == 0 || current_bootversion == 1);
-  if (requires_new_bootversion)
-    *out_new_bootversion = (current_bootversion == 0) ? 1 : 0;
-  else
-    *out_new_bootversion = current_bootversion;
-}
-
-static GHashTable *
-object_array_to_set (GPtrArray   *objlist,
-                     GHashFunc    hashfunc,
-                     GEqualFunc   equalfunc)
-{
-  GHashTable *ret = g_hash_table_new_full (hashfunc, equalfunc, g_object_unref, NULL);
-  guint i;
-
-  for (i = 0; i < objlist->len; i++)
-    {
-      GObject *obj = g_object_ref (objlist->pdata[i]);
-      g_hash_table_insert (ret, obj, obj);
-    }
-  
-  return ret;
-}
-
-static GHashTable *
-object_set_subtract (GHashTable *a, GHashTable *b)
-{
-  GHashTable *ret = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
-  GHashTableIter hashiter;
-  gpointer hashkey, hashvalue;
-  
-  g_hash_table_iter_init (&hashiter, a);
-  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
-    {
-      if (!g_hash_table_contains (b, hashkey))
-        {
-          GObject *o = g_object_ref (hashkey);
-          g_hash_table_insert (ret, o, o);
-        }
-    }
-
-  return ret;
-}
-
-static void
-print_deployment_set (gboolean    for_removal,
-                      GHashTable *set)
-{
-  GHashTableIter hashiter;
-  gpointer hashkey, hashvalue;
-
-  if (g_hash_table_size (set) == 0)
-    return;
-
-  g_print ("%s\n", for_removal ? "removed:" : "added: ");
-
-  g_hash_table_iter_init (&hashiter, set);
-  while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue))
-    {
-      OstreeDeployment *deployment = hashkey;
-
-      g_print ("  %c %s %s.%d",
-               for_removal ? '-' : '+', ostree_deployment_get_osname (deployment),
-               ostree_deployment_get_csum (deployment),
-               ostree_deployment_get_deployserial (deployment));
-
-      if (!for_removal)
-        g_print (" index=%d", ostree_deployment_get_index (deployment));
-      g_print ("\n");
-    }
-}
-
-static void
-print_deployment_diff (GPtrArray   *current_deployments,
-                       GPtrArray   *new_deployments)
-{
-  gs_unref_hashtable GHashTable *curset = object_array_to_set (current_deployments, ostree_deployment_hash, ostree_deployment_equal);
-  gs_unref_hashtable GHashTable *newset = object_array_to_set (new_deployments, ostree_deployment_hash, ostree_deployment_equal);
-  gs_unref_hashtable GHashTable *removed = NULL;
-  gs_unref_hashtable GHashTable *added = NULL;
-
-  removed = object_set_subtract (curset, newset);
-  added = object_set_subtract (newset, curset);
-
-  print_deployment_set (TRUE, removed);
-  print_deployment_set (FALSE, added);
-}
-
-/* FIXME: We should really do individual fdatasync() on files/dirs,
- * since this causes us to block on unrelated I/O.  However, it's just
- * safer for now.
- */
-static gboolean
-full_system_sync (GCancellable      *cancellable,
-                  GError           **error)
-{
-  sync ();
-  return TRUE;
-}
-
-static gboolean
-swap_bootlinks (OstreeSysroot *sysroot,
-                int           current_bootversion,
-                GPtrArray    *new_deployments,
-                GCancellable *cancellable,
-                GError      **error)
-{
-  gboolean ret = FALSE;
-  guint i;
-  int old_subbootversion, new_subbootversion;
-  gs_unref_object GFile *ostree_dir = g_file_get_child (ostree_sysroot_get_path (sysroot), "ostree");
-  gs_free char *ostree_bootdir_name = g_strdup_printf ("boot.%d", current_bootversion);
-  gs_unref_object GFile *ostree_bootdir = g_file_resolve_relative_path (ostree_dir, ostree_bootdir_name);
-  gs_free char *ostree_subbootdir_name = NULL;
-  gs_unref_object GFile *ostree_subbootdir = NULL;
-
-  if (!ostree_sysroot_read_current_subbootversion (sysroot, current_bootversion,
-                                                   &old_subbootversion,
-                                                   cancellable, error))
-    goto out;
-
-  new_subbootversion = old_subbootversion == 0 ? 1 : 0;
-
-  ostree_subbootdir_name = g_strdup_printf ("boot.%d.%d", current_bootversion, new_subbootversion);
-  ostree_subbootdir = g_file_resolve_relative_path (ostree_dir, ostree_subbootdir_name);
-
-  if (!gs_file_ensure_directory (ostree_subbootdir, TRUE, cancellable, error))
-    goto out;
-
-  for (i = 0; i < new_deployments->len; i++)
-    {
-      OstreeDeployment *deployment = new_deployments->pdata[i];
-      gs_free char *bootlink_pathname = g_strdup_printf ("%s/%s/%d",
-                                                         ostree_deployment_get_osname (deployment),
-                                                         ostree_deployment_get_bootcsum (deployment),
-                                                         ostree_deployment_get_bootserial (deployment));
-      gs_free char *bootlink_target = g_strdup_printf ("../../../deploy/%s/deploy/%s.%d",
-                                                       ostree_deployment_get_osname (deployment),
-                                                       ostree_deployment_get_csum (deployment),
-                                                       ostree_deployment_get_deployserial (deployment));
-      gs_unref_object GFile *linkname = g_file_get_child (ostree_subbootdir, bootlink_pathname);
-      gs_unref_object GFile *linkname_parent = g_file_get_parent (linkname);
-
-      if (!gs_file_ensure_directory (linkname_parent, TRUE, cancellable, error))
-        goto out;
-
-      if (!g_file_make_symbolic_link (linkname, bootlink_target, cancellable, error))
-        goto out;
-    }
-
-  if (!ot_gfile_atomic_symlink_swap (ostree_bootdir, ostree_subbootdir_name,
-                                     cancellable, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static char *
-remove_checksum_from_kernel_name (const char *name,
-                                  const char *csum)
-{
-  const char *p = strrchr (name, '-');
-  g_assert_cmpstr (p+1, ==, csum);
-  return g_strndup (name, p-name);
-}
-
-static GHashTable *
-parse_os_release (const char *contents,
-                  const char *split)
-{
-  char **lines = g_strsplit (contents, split, -1);
-  char **iter;
-  GHashTable *ret = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
-
-  for (iter = lines; *iter; iter++)
-    {
-      char *line = *iter;
-      char *eq;
-      const char *quotedval;
-      char *val;
-
-      if (g_str_has_prefix (line, "#"))
-        continue;
-      
-      eq = strchr (line, '=');
-      if (!eq)
-        continue;
-      
-      *eq = '\0';
-      quotedval = eq + 1;
-      val = g_shell_unquote (quotedval, NULL);
-      if (!val)
-        continue;
-      
-      g_hash_table_insert (ret, line, val);
-    }
-
-  return ret;
-}
-
-/*
- * install_deployment_kernel:
- * 
- * Write out an entry in /boot/loader/entries for @deployment.
- */
-static gboolean
-install_deployment_kernel (OstreeSysroot   *sysroot,
-                           int             new_bootversion,
-                           OstreeDeployment   *deployment,
-                           guint           n_deployments,
-                           GCancellable   *cancellable,
-                           GError        **error)
-
-{
-  gboolean ret = FALSE;
-  const char *osname = ostree_deployment_get_osname (deployment);
-  const char *bootcsum = ostree_deployment_get_bootcsum (deployment);
-  gs_unref_object GFile *bootdir = NULL;
-  gs_unref_object GFile *bootcsumdir = NULL;
-  gs_unref_object GFile *bootconfpath = NULL;
-  gs_unref_object GFile *bootconfpath_parent = NULL;
-  gs_free char *dest_kernel_name = NULL;
-  gs_unref_object GFile *dest_kernel_path = NULL;
-  gs_unref_object GFile *dest_initramfs_path = NULL;
-  gs_unref_object GFile *tree_kernel_path = NULL;
-  gs_unref_object GFile *tree_initramfs_path = NULL;
-  gs_unref_object GFile *etc_os_release = NULL;
-  gs_unref_object GFile *deployment_dir = NULL;
-  gs_free char *contents = NULL;
-  gs_unref_hashtable GHashTable *osrelease_values = NULL;
-  gs_free char *linux_relpath = NULL;
-  gs_free char *linux_key = NULL;
-  gs_free char *initramfs_relpath = NULL;
-  gs_free char *title_key = NULL;
-  gs_free char *initrd_key = NULL;
-  gs_free char *version_key = NULL;
-  gs_free char *ostree_kernel_arg = NULL;
-  gs_free char *options_key = NULL;
-  __attribute__((cleanup(ot_ordered_hash_cleanup))) OtOrderedHash *ohash = NULL;
-  const char *val;
-  OstreeBootconfigParser *bootconfig;
-  gsize len;
-
-  bootconfig = ostree_deployment_get_bootconfig (deployment);
-  deployment_dir = ostree_sysroot_get_deployment_directory (sysroot, deployment);
-
-  if (!get_kernel_from_tree (deployment_dir, &tree_kernel_path, &tree_initramfs_path,
-                             cancellable, error))
-    goto out;
-
-  bootdir = g_file_get_child (ostree_sysroot_get_path (sysroot), "boot");
-  bootcsumdir = ot_gfile_resolve_path_printf (bootdir, "ostree/%s-%s",
-                                              osname,
-                                              bootcsum);
-  bootconfpath = ot_gfile_resolve_path_printf (bootdir, "loader.%d/entries/ostree-%s-%s-%d.conf",
-                                               new_bootversion, osname, 
-                                               ostree_deployment_get_csum (deployment),
-                                               ostree_deployment_get_bootserial (deployment));
-
-  if (!gs_file_ensure_directory (bootcsumdir, TRUE, cancellable, error))
-    goto out;
-  bootconfpath_parent = g_file_get_parent (bootconfpath);
-  if (!gs_file_ensure_directory (bootconfpath_parent, TRUE, cancellable, error))
-    goto out;
-
-  dest_kernel_name = remove_checksum_from_kernel_name (gs_file_get_basename_cached (tree_kernel_path),
-                                                       bootcsum);
-  dest_kernel_path = g_file_get_child (bootcsumdir, dest_kernel_name);
-  if (!g_file_query_exists (dest_kernel_path, NULL))
-    {
-      if (!gs_file_linkcopy_sync_data (tree_kernel_path, dest_kernel_path, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
-                                       cancellable, error))
-        goto out;
-    }
-
-  if (tree_initramfs_path)
-    {
-      gs_free char *dest_initramfs_name = remove_checksum_from_kernel_name (gs_file_get_basename_cached (tree_initramfs_path),
-                                                                       bootcsum);
-      dest_initramfs_path = g_file_get_child (bootcsumdir, dest_initramfs_name);
-
-      if (!g_file_query_exists (dest_initramfs_path, NULL))
-        {
-          if (!gs_file_linkcopy_sync_data (tree_initramfs_path, dest_initramfs_path, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
-                                           cancellable, error))
-            goto out;
-        }
-    }
-
-  etc_os_release = g_file_resolve_relative_path (deployment_dir, "etc/os-release");
-
-  if (!g_file_load_contents (etc_os_release, cancellable,
-                             &contents, &len, NULL, error))
-    {
-      g_prefix_error (error, "Reading /etc/os-release: ");
-      goto out;
-    }
-
-  osrelease_values = parse_os_release (contents, "\n");
-
-  /* title */
-  val = g_hash_table_lookup (osrelease_values, "PRETTY_NAME");
-  if (val == NULL)
-      val = g_hash_table_lookup (osrelease_values, "ID");
-  if (val == NULL)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "No PRETTY_NAME or ID in /etc/os-release");
-      goto out;
-    }
-  
-  title_key = g_strdup_printf ("ostree:%s:%d %s", ostree_deployment_get_osname (deployment),
-                               ostree_deployment_get_index (deployment),
-                               val);
-  ostree_bootconfig_parser_set (bootconfig, "title", title_key);
-
-  version_key = g_strdup_printf ("%d", n_deployments - ostree_deployment_get_index (deployment));
-  ostree_bootconfig_parser_set (bootconfig, "version", version_key);
-
-  linux_relpath = g_file_get_relative_path (bootdir, dest_kernel_path);
-  linux_key = g_strconcat ("/", linux_relpath, NULL);
-  ostree_bootconfig_parser_set (bootconfig, "linux", linux_key);
-
-  if (dest_initramfs_path)
-    {
-      initramfs_relpath = g_file_get_relative_path (bootdir, dest_initramfs_path);
-      initrd_key = g_strconcat ("/", initramfs_relpath, NULL);
-      ostree_bootconfig_parser_set (bootconfig, "initrd", initrd_key);
-    }
-
-  val = ostree_bootconfig_parser_get (bootconfig, "options");
-  ostree_kernel_arg = g_strdup_printf ("/ostree/boot.%d/%s/%s/%d",
-                                       new_bootversion, osname, bootcsum,
-                                       ostree_deployment_get_bootserial (deployment));
-  ohash = ot_admin_parse_kernel_args (val);
-  ot_ordered_hash_replace_key (ohash, "ostree", ostree_kernel_arg);
-  options_key = ot_admin_kernel_arg_string_serialize (ohash);
-  ostree_bootconfig_parser_set (bootconfig, "options", options_key);
-  
-  if (!ostree_bootconfig_parser_write (ostree_deployment_get_bootconfig (deployment), bootconfpath,
-                               cancellable, error))
-      goto out;
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-swap_bootloader (GFile          *sysroot,
-                 int             current_bootversion,
-                 int             new_bootversion,
-                 GCancellable   *cancellable,
-                 GError        **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *boot_loader_link = NULL;
-  gs_free char *new_target = NULL;
-
-  g_assert ((current_bootversion == 0 && new_bootversion == 1) ||
-            (current_bootversion == 1 && new_bootversion == 0));
-
-  boot_loader_link = g_file_resolve_relative_path (sysroot, "boot/loader");
-  new_target = g_strdup_printf ("loader.%d", new_bootversion);
-
-  if (!ot_gfile_atomic_symlink_swap (boot_loader_link, new_target,
-                                     cancellable, error))
-    goto out;
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-gboolean
-ot_admin_write_deployments (GFile             *sysroot,
-                            int                current_bootversion,
-                            int                new_bootversion,
-                            GPtrArray         *new_deployments,
-                            GCancellable      *cancellable,
-                            GError           **error)
-{
-  gboolean ret = FALSE;
-  guint i;
-  gs_unref_object OstreeSysroot *sysroot_obj = ostree_sysroot_new (sysroot);
-  gs_unref_object OstreeBootloader *bootloader = ostree_sysroot_query_bootloader (sysroot_obj);
-
-  if (bootloader)
-    g_print ("Detected bootloader: %s\n", ostree_bootloader_get_name (bootloader));
-  else
-    g_print ("Detected bootloader: (unknown)\n");
-
-  if (current_bootversion == new_bootversion)
-    {
-      if (!full_system_sync (cancellable, error))
-        {
-          g_prefix_error (error, "Full sync: ");
-          goto out;
-        }
-
-      if (!swap_bootlinks (sysroot_obj, current_bootversion,
-                           new_deployments,
-                           cancellable, error))
-        {
-          g_prefix_error (error, "Swapping current bootlinks: ");
-          goto out;
-        }
-    }
-  else
-    {
-      for (i = 0; i < new_deployments->len; i++)
-        {
-          OstreeDeployment *deployment = new_deployments->pdata[i];
-          if (!install_deployment_kernel (sysroot_obj, new_bootversion,
-                                          deployment, new_deployments->len,
-                                          cancellable, error))
-            {
-              g_prefix_error (error, "Installing kernel: ");
-              goto out;
-            }
-        }
-
-      /* Swap bootlinks for *new* version */
-      if (!swap_bootlinks (sysroot_obj, new_bootversion, new_deployments,
-                           cancellable, error))
-        {
-          g_prefix_error (error, "Generating new bootlinks: ");
-          goto out;
-        }
-
-      if (!full_system_sync (cancellable, error))
-        {
-          g_prefix_error (error, "Full sync: ");
-          goto out;
-        }
-
-      if (bootloader && !ostree_bootloader_write_config (bootloader, new_bootversion,
-                                                         cancellable, error))
-          {
-            g_prefix_error (error, "Bootloader write config: ");
-            goto out;
-          }
-
-      if (!swap_bootloader (sysroot, current_bootversion, new_bootversion,
-                            cancellable, error))
-        {
-          g_prefix_error (error, "Final bootloader swap: ");
-          goto out;
-        }
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-                            
-gboolean
-ot_admin_deploy (GFile             *sysroot,
-                 int                current_bootversion,
-                 GPtrArray         *current_deployments,
-                 const char        *osname,
-                 const char        *revision,
-                 GKeyFile          *origin,
-                 char             **add_kernel_argv,
-                 gboolean           retain,
-                 OstreeDeployment      *booted_deployment,
-                 OstreeDeployment      *provided_merge_deployment,
-                 OstreeDeployment     **out_new_deployment,
-                 int               *out_new_bootversion,
-                 GPtrArray        **out_new_deployments,
-                 GCancellable      *cancellable,
-                 GError           **error)
-{
-  gboolean ret = FALSE;
-  OstreeDeployment *new_deployment;
-  gs_unref_object OstreeSysroot *sysroot_obj = NULL;
-  gs_unref_object OstreeDeployment *merge_deployment = NULL;
-  gs_unref_object OstreeRepo *repo = NULL;
-  gs_unref_object GFile *commit_root = NULL;
-  gs_unref_object GFile *tree_kernel_path = NULL;
-  gs_unref_object GFile *tree_initramfs_path = NULL;
-  gs_unref_object GFile *new_deployment_path = NULL;
-  gs_free char *new_bootcsum = NULL;
-  gs_unref_object OstreeBootconfigParser *bootconfig = NULL;
-  gs_unref_ptrarray GPtrArray *new_deployments = NULL;
-  int new_bootversion;
-
-  sysroot_obj = ostree_sysroot_new (sysroot);
-
-  if (!ostree_sysroot_get_repo (sysroot_obj, &repo, cancellable, error))
-    goto out;
-
-  /* Here we perform cleanup of any leftover data from previous
-   * partial failures.  This avoids having to call gs_shutil_rm_rf()
-   * at random points throughout the process.
-   *
-   * TODO: Add /ostree/transaction file, and only do this cleanup if
-   * we find it.
-   */
-  if (!ostree_sysroot_cleanup (sysroot_obj, cancellable, error))
-    {
-      g_prefix_error (error, "Performing initial cleanup: ");
-      goto out;
-    }
-
-  if (!ostree_repo_read_commit (repo, revision, &commit_root, NULL, cancellable, error))
-    goto out;
-
-  if (!get_kernel_from_tree (commit_root, &tree_kernel_path, &tree_initramfs_path,
-                             cancellable, error))
-    goto out;
-  
-  if (tree_initramfs_path != NULL)
-    {
-      if (!checksum_from_kernel_src (tree_initramfs_path, &new_bootcsum, error))
-        goto out;
-    }
-  else
-    {
-      if (!checksum_from_kernel_src (tree_kernel_path, &new_bootcsum, error))
-        goto out;
-    }
-
-  /* If we're booted into the OS into which we're deploying, then
-   * merge the currently *booted* configuration, rather than the most
-   * recently deployed.
-   */
-  if (provided_merge_deployment != NULL)
-    merge_deployment = g_object_ref (provided_merge_deployment);
-  else
-    merge_deployment = ot_admin_get_merge_deployment (current_deployments, osname,
-                                                      booted_deployment); 
-
-  compute_new_deployment_list (current_bootversion,
-                               current_deployments, osname,
-                               booted_deployment, merge_deployment,
-                               retain,
-                               revision, new_bootcsum,
-                               &new_deployments,
-                               &new_bootversion);
-  new_deployment = g_object_ref (new_deployments->pdata[0]);
-  ostree_deployment_set_origin (new_deployment, origin);
-
-  print_deployment_diff (current_deployments, new_deployments);
-
-  /* Check out the userspace tree onto the filesystem */
-  if (!checkout_deployment_tree (sysroot, repo, new_deployment, &new_deployment_path,
-                                 cancellable, error))
-    {
-      g_prefix_error (error, "Checking out tree: ");
-      goto out;
-    }
-
-  if (!write_origin_file (sysroot, new_deployment, cancellable, error))
-    {
-      g_prefix_error (error, "Writing out origin file: ");
-      goto out;
-    }
-
-  /* Create an empty boot configuration; we will merge things into
-   * it as we go.
-   */
-  bootconfig = ostree_bootconfig_parser_new ();
-  ostree_deployment_set_bootconfig (new_deployment, bootconfig);
-
-  if (!merge_configuration (sysroot_obj, merge_deployment, new_deployment,
-                            new_deployment_path,
-                            cancellable, error))
-    {
-      g_prefix_error (error, "During /etc merge: ");
-      goto out;
-    }
-
-  /* We have inherited kernel arguments from the previous deployment;
-   * now, override/extend that with arguments provided by the command
-   * line.
-   * 
-   * After this, install_deployment_kernel() will set the other boot
-   * options and write it out to disk.
-   */
-  if (add_kernel_argv)
-    {
-      char **strviter;
-      __attribute__((cleanup(ot_ordered_hash_cleanup))) OtOrderedHash *ohash = NULL;
-      gs_free char *new_options = NULL;
-
-      ohash = ot_admin_parse_kernel_args (ostree_bootconfig_parser_get (bootconfig, "options"));
-
-      for (strviter = add_kernel_argv; *strviter; strviter++)
-        {
-          char *karg = g_strdup (*strviter);
-          const char *val = ot_admin_util_split_keyeq (karg);
-        
-          ot_ordered_hash_replace_key_take (ohash, karg, val);
-        }
-
-      new_options = ot_admin_kernel_arg_string_serialize (ohash);
-      ostree_bootconfig_parser_set (bootconfig, "options", new_options);
-    }
-
-  if (!ot_admin_write_deployments (sysroot, current_bootversion, new_bootversion,
-                                   new_deployments, cancellable, error))
-    goto out;
-
-  g_print ("Transaction complete, performing cleanup\n");
-
-  /* TEMPORARY HACK: Add a "current" symbolic link that's easy to
-   * follow inside the gnome-ostree build scripts.  This isn't atomic,
-   * but that doesn't matter because it's only used by deployments
-   * done from the host.
-   */
-  {
-    gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (sysroot, "ostree/deploy/%s", ostree_deployment_get_osname (new_deployment));
-    gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current");
-    gs_free char *target = g_file_get_relative_path (osdir, new_deployment_path);
-    g_assert (target != NULL);
-    if (!ot_gfile_atomic_symlink_swap (os_current_path, target,
-                                       cancellable, error))
-      goto out;
-  }
-
-  /* And finally, cleanup of any leftover data.
-   */
-  if (!ostree_sysroot_cleanup (sysroot_obj, cancellable, error))
-    {
-      g_prefix_error (error, "Performing final cleanup: ");
-      goto out;
-    }
-
-  ret = TRUE;
-  ot_transfer_out_value (out_new_deployment, &new_deployment);
-  *out_new_bootversion = new_bootversion;
-  ot_transfer_out_value (out_new_deployments, &new_deployments)
- out:
-  return ret;
-}
-
diff --git a/src/ostree/ot-admin-deploy.h b/src/ostree/ot-admin-deploy.h
deleted file mode 100644 (file)
index 6652201..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2012 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#pragma once
-
-#include <gio/gio.h>
-#include <ostree.h>
-#include "ot-ordered-hash.h"
-
-G_BEGIN_DECLS
-
-gboolean ot_admin_write_deployments (GFile             *sysroot,
-                                     int                current_bootversion,
-                                     int                new_bootversion,
-                                     GPtrArray         *new_deployments,
-                                     GCancellable      *cancellable,
-                                     GError           **error);
-
-gboolean ot_admin_deploy (GFile             *sysroot,
-                          int                current_bootversion,
-                          GPtrArray         *current_deployments,
-                          const char        *osname,
-                          const char        *revision,
-                          GKeyFile          *origin,
-                          char             **add_kernel_argv,
-                          gboolean           retain,
-                          OstreeDeployment      *booted_deployment,
-                          OstreeDeployment      *merge_deployment,
-                          OstreeDeployment     **out_new_deployment,
-                          int               *out_new_bootversion,
-                          GPtrArray        **out_new_deployments,
-                          GCancellable      *cancellable,
-                          GError           **error);
-
-G_END_DECLS
-
index f497fcbe5c853a7d88cd6e8020515f4a363b223f..c72c15b522940b5555d0049e7adce7e7f662b801 100644 (file)
 #include "ostree.h"
 #include "libgsystem.h"
 
-OtOrderedHash *
-ot_admin_parse_kernel_args (const char *options)
-{
-  OtOrderedHash *ret;
-  char **args;
-  char **iter;
-
-  ret = ot_ordered_hash_new ();
-
-  if (!options)
-    return ret;
-  
-  args = g_strsplit (options, " ", -1);
-  for (iter = args; *iter; iter++)
-    {
-      char *arg = *iter;
-      char *val;
-      
-      val = ot_admin_util_split_keyeq (arg);
-
-      g_ptr_array_add (ret->order, arg);
-      g_hash_table_insert (ret->table, arg, val);
-    }
-
-  return ret;
-}
-
-char *
-ot_admin_kernel_arg_string_serialize (OtOrderedHash *ohash)
-{
-  guint i;
-  GString *buf = g_string_new ("");
-  gboolean first = TRUE;
-
-  for (i = 0; i < ohash->order->len; i++)
-    {
-      const char *key = ohash->order->pdata[i];
-      const char *val = g_hash_table_lookup (ohash->table, key);
-
-      g_assert (val != NULL);
-
-      if (first)
-        first = FALSE;
-      else
-        g_string_append_c (buf, ' ');
-
-      if (*val)
-        g_string_append_printf (buf, "%s=%s", key, val);
-      else
-        g_string_append (buf, key);
-    }
-
-  return g_string_free (buf, FALSE);
-}
-
-gboolean
-ot_admin_check_os (GFile         *sysroot, 
-                   const char    *osname,
-                   GCancellable  *cancellable,
-                   GError       **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *osdir = NULL;
-
-  osdir = ot_gfile_resolve_path_printf (sysroot, "ostree/deploy/%s/var", osname);
-  if (!g_file_query_exists (osdir, NULL))
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "No such OS '%s', use os-init to create it", osname);
-      goto out;
-    }
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
-static gboolean
-parse_kernel_commandline (OtOrderedHash  **out_args,
-                          GCancellable    *cancellable,
-                          GError         **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *proc_cmdline = g_file_new_for_path ("/proc/cmdline");
-  gs_free char *contents = NULL;
-  gsize len;
-
-  if (!g_file_load_contents (proc_cmdline, cancellable, &contents, &len, NULL,
-                             error))
-    goto out;
-
-  ret = TRUE;
-  *out_args = ot_admin_parse_kernel_args (contents);;
- out:
-  return ret;
-}
-
-/**
- * ot_admin_find_booted_deployment:
- * @target_sysroot: Root directory
- * @deployments: (element-type OstreeDeployment): Loaded deployments
- * @out_deployment: (out): The currently booted deployment
- * @cancellable:
- * @error: 
- * 
- * If the system is currently booted into a deployment in
- * @deployments, set @out_deployment.  Note that if @target_sysroot is
- * not equal to "/", @out_deployment will always be set to %NULL.
- */
-gboolean
-ot_admin_find_booted_deployment (GFile               *target_sysroot,
-                                 GPtrArray           *deployments,
-                                 OstreeDeployment       **out_deployment,
-                                 GCancellable        *cancellable,
-                                 GError             **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFile *active_root = g_file_new_for_path ("/");
-  gs_unref_object OstreeSysroot *active_deployment_root = ostree_sysroot_new_default ();
-  gs_unref_object OstreeDeployment *ret_deployment = NULL;
-
-  if (g_file_equal (active_root, target_sysroot))
-    { 
-      guint i;
-      const char *bootlink_arg;
-      __attribute__((cleanup(ot_ordered_hash_cleanup))) OtOrderedHash *kernel_args = NULL;
-      guint32 root_device;
-      guint64 root_inode;
-      
-      if (!ot_admin_util_get_devino (active_root, &root_device, &root_inode,
-                                     cancellable, error))
-        goto out;
-
-      if (!parse_kernel_commandline (&kernel_args, cancellable, error))
-        goto out;
-      
-      bootlink_arg = g_hash_table_lookup (kernel_args->table, "ostree");
-      if (bootlink_arg)
-        {
-          for (i = 0; i < deployments->len; i++)
-            {
-              OstreeDeployment *deployment = deployments->pdata[i];
-              gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (active_deployment_root, deployment);
-              guint32 device;
-              guint64 inode;
-
-              if (!ot_admin_util_get_devino (deployment_path, &device, &inode,
-                                             cancellable, error))
-                goto out;
-
-              if (device == root_device && inode == root_inode)
-                {
-                  ret_deployment = g_object_ref (deployment);
-                  break;
-                }
-            }
-          if (ret_deployment == NULL)
-            {
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Unexpected state: ostree= kernel argument found, but / is not a deployment root");
-              goto out;
-            }
-        }
-      else
-        {
-          /* Not an ostree system */
-        }
-    }
-
-  ret = TRUE;
-  ot_transfer_out_value (out_deployment, &ret_deployment);
- out:
-  return ret;
-}
-
-gboolean
-ot_admin_require_deployment_or_osname (GFile               *sysroot,
-                                       GPtrArray           *deployments,
-                                       const char          *osname,
-                                       OstreeDeployment       **out_deployment,
-                                       GCancellable        *cancellable,
-                                       GError             **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object OstreeDeployment *ret_deployment = NULL;
-
-  if (!ot_admin_find_booted_deployment (sysroot, deployments, &ret_deployment,
-                                        cancellable, error))
-    goto out;
-
-  if (ret_deployment == NULL && osname == NULL)
-    {
-      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Not currently booted into an OSTree system and no --os= argument given");
-      goto out;
-    }
-  
-  ret = TRUE;
-  ot_transfer_out_value (out_deployment, &ret_deployment);
- out:
-  return ret;
-}
-
-OstreeDeployment *
-ot_admin_get_merge_deployment (GPtrArray         *deployments,
-                               const char        *osname,
-                               OstreeDeployment      *booted_deployment)
-{
-  g_return_val_if_fail (osname != NULL || booted_deployment != NULL, NULL);
-
-  if (osname == NULL)
-    osname = ostree_deployment_get_osname (booted_deployment);
-
-  if (booted_deployment &&
-      g_strcmp0 (ostree_deployment_get_osname (booted_deployment), osname) == 0)
-    {
-      return g_object_ref (booted_deployment);
-    }
-  else
-    {
-      guint i;
-      for (i = 0; i < deployments->len; i++)
-        {
-          OstreeDeployment *deployment = deployments->pdata[i];
-
-          if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0)
-            continue;
-          
-          return g_object_ref (deployment);
-        }
-    }
-  return NULL;
-}
-
 GKeyFile *
 ot_origin_new_from_refspec (const char *refspec)
 {
index 4b7a6a68d013bed61fba00a80ff8ee46be8c5fed..189974f6df34deb9687bfe6cfdf6ac823c0b6f89 100644 (file)
 
 #include <gio/gio.h>
 #include <ostree.h>
-#include "ot-ordered-hash.h"
 
 G_BEGIN_DECLS
 
-char *ot_admin_util_split_keyeq (char *str);
-
-gboolean ot_admin_util_get_devino (GFile         *path,
-                                   guint32       *out_device,
-                                   guint64       *out_inode,
-                                   GCancellable  *cancellable,
-                                   GError       **error);
-
-gboolean ot_admin_ensure_initialized (GFile         *ostree_dir, 
-                                     GCancellable  *cancellable,
-                                     GError       **error);
-
-gboolean ot_admin_check_os (GFile         *sysroot, 
-                            const char    *osname,
-                            GCancellable  *cancellable,
-                            GError       **error);
-
-OtOrderedHash *ot_admin_parse_kernel_args (const char *options);
-char * ot_admin_kernel_arg_string_serialize (OtOrderedHash *ohash);
-
-gboolean ot_admin_find_booted_deployment (GFile               *sysroot,
-                                          GPtrArray           *deployments,
-                                          OstreeDeployment       **out_deployment,
-                                          GCancellable        *cancellable,
-                                          GError             **error);
-
-gboolean ot_admin_require_booted_deployment (GFile               *sysroot,
-                                             OstreeDeployment       **out_deployment,
-                                             GCancellable        *cancellable,
-                                             GError             **error);
-
-gboolean ot_admin_require_deployment_or_osname (GFile               *sysroot,
-                                                GPtrArray           *deployment_list,
-                                                const char          *osname,
-                                                OstreeDeployment       **out_deployment,
-                                                GCancellable        *cancellable,
-                                                GError             **error);
-
-OstreeDeployment *ot_admin_get_merge_deployment (GPtrArray         *deployment_list,
-                                             const char        *osname,
-                                             OstreeDeployment      *booted_deployment);
-
-gboolean ot_admin_get_default_ostree_dir (GFile        **out_ostree_dir,
-                                          GCancellable  *cancellable,
-                                          GError       **error);
-
 GKeyFile *ot_origin_new_from_refspec (const char *refspec);
 
 G_END_DECLS
diff --git a/src/ostree/ot-admin-util.c b/src/ostree/ot-admin-util.c
deleted file mode 100644 (file)
index 5e82261..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2012 Colin Walters <walters@verbum.org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- * Author: Colin Walters <walters@verbum.org>
- */
-
-#include "config.h"
-
-#include "ot-admin-functions.h"
-#include "otutil.h"
-#include "ostree-core.h"
-#include "libgsystem.h"
-
-/*
- * Modify @arg which should be of the form key=value to make @arg just
- * contain key.  Return a pointer to the start of value.
- */
-char *
-ot_admin_util_split_keyeq (char *arg)
-{
-  char *eq;
-      
-  eq = strchr (arg, '=');
-  if (eq)
-    {
-      /* Note key/val are in one malloc block,
-       * so we don't free val...
-       */
-      *eq = '\0';
-      return eq+1;
-    }
-  else
-    {
-      /* ...and this allows us to insert a constant
-       * string.
-       */
-      return "";
-    }
-}
-
-gboolean
-ot_admin_util_get_devino (GFile         *path,
-                          guint32       *out_device,
-                          guint64       *out_inode,
-                          GCancellable  *cancellable,
-                          GError       **error)
-{
-  gboolean ret = FALSE;
-  gs_unref_object GFileInfo *finfo = g_file_query_info (path, "unix::device,unix::inode",
-                                                        G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
-                                                        cancellable, error);
-
-  if (!finfo)
-    goto out;
-
-  ret = TRUE;
-  *out_device = g_file_info_get_attribute_uint32 (finfo, "unix::device");
-  *out_inode = g_file_info_get_attribute_uint64 (finfo, "unix::inode");
- out:
-  return ret;
-}
-
diff --git a/src/ostree/ot-ordered-hash.c b/src/ostree/ot-ordered-hash.c
deleted file mode 100644 (file)
index 02c9e43..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2013 Colin Walters <walters@verbum.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2 of the licence or (at
- * your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "config.h"
-
-#include "ot-ordered-hash.h"
-#include "libgsystem.h"
-
-OtOrderedHash *
-ot_ordered_hash_new (void)
-{
-  OtOrderedHash *ret;
-  ret = g_new0 (OtOrderedHash, 1);
-  ret->order = g_ptr_array_new_with_free_func (g_free);
-  ret->table = g_hash_table_new (g_str_hash, g_str_equal);
-  return ret;
-}
-
-void
-ot_ordered_hash_free (OtOrderedHash *ohash)
-{
-  if (!ohash)
-    return;
-  g_ptr_array_unref (ohash->order);
-  g_hash_table_unref (ohash->table);
-  g_free (ohash);
-}
-
-void
-ot_ordered_hash_cleanup (void *loc)
-{
-  ot_ordered_hash_free (*((OtOrderedHash**)loc));
-}
-
-void
-ot_ordered_hash_replace_key_take (OtOrderedHash   *ohash,
-                                  char            *key,
-                                  const char      *value)
-{
-  gboolean existed;
-
-  existed = g_hash_table_remove (ohash->table, key);
-  if (!existed)
-    g_ptr_array_add (ohash->order, key);
-  g_hash_table_insert (ohash->table, key, (char*)value);
-}
-
-void
-ot_ordered_hash_replace_key (OtOrderedHash  *ohash,
-                             const char     *key,
-                             const char     *val)
-{
-  GString *buf;
-  gsize keylen;
-  char *valp;
-  char *valblock;
-  
-  buf = g_string_new (key);
-  keylen = buf->len;
-  g_string_append_c (buf, '\0');
-  g_string_append (buf, val);
-  valblock = g_string_free (buf, FALSE);
-  valp = valblock + keylen + 1;
-
-  ot_ordered_hash_replace_key_take (ohash, valblock, valp);
-}
diff --git a/src/ostree/ot-ordered-hash.h b/src/ostree/ot-ordered-hash.h
deleted file mode 100644 (file)
index 1a3aa09..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
- *
- * Copyright (C) 2013 Colin Walters <walters@verbum.org>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; either version 2 of the licence or (at
- * your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#pragma once
-
-#include <gio/gio.h>
-
-G_BEGIN_DECLS
-
-typedef struct {
-  GPtrArray  *order;
-  GHashTable *table;
-} OtOrderedHash;
-
-OtOrderedHash *ot_ordered_hash_new (void);
-void ot_ordered_hash_free (OtOrderedHash *ohash);
-void ot_ordered_hash_cleanup (void *loc);
-void ot_ordered_hash_replace_key_take (OtOrderedHash  *ohash,
-                                       char         *key,
-                                       const char   *value);
-void ot_ordered_hash_replace_key (OtOrderedHash  *ohash,
-                                  const char   *key,
-                                  const char   *val);
-
-
-G_END_DECLS
-